diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json deleted file mode 100644 index c6df74dd3..000000000 --- a/.codeqlmanifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "provide": [ - "ql/src/qlpack.yml", - "ql/lib/qlpack.yml", - "ql/examples/qlpack.yml", - "ql/test/qlpack.yml", - "ql/config/legacy-support/qlpack.yml", - "build/codeql-extractor-go/codeql-extractor.yml" - ], - "ignore": [ - "the-extractor-which-needs-to-be-built" - ], - "versionPolicies": { - "default": { - "requireChangeNotes": true, - "committedPrereleaseSuffix": "dev", - "committedVersion": "nextPatchRelease" - } - } -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 973a584ac..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extensions": [ - "github.vscode-codeql", - "slevesque.vscode-zipexplorer" - ], - "settings": { - "codeQL.runningQueries.memory": 2048, - "codeQL.runningQueries.debug": true - } -} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index fd2c3bc60..000000000 --- a/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# Force git not to modify line endings for go or html files under the ql directory -ql/**/*.go -text -ql/**/*.html -text - -# Force git not to modify line endings for dbschemes -*.dbscheme -text diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml deleted file mode 100644 index 4f21e2ef6..000000000 --- a/.github/codeql/codeql-config.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: "CodeQL config" - -queries: - - uses: security-and-quality diff --git a/.github/problem-matchers/codeql-query-format.json b/.github/problem-matchers/codeql-query-format.json deleted file mode 100644 index 35f9c1a6f..000000000 --- a/.github/problem-matchers/codeql-query-format.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "codeql-query-format", - "pattern": [ - { - "regexp": "^((.*) would change by autoformatting\\.)$", - "file": 2, - "message": 1 - } - ] - } - ] -} diff --git a/.github/problem-matchers/codeql-syntax-check.json b/.github/problem-matchers/codeql-syntax-check.json deleted file mode 100644 index d285ffd77..000000000 --- a/.github/problem-matchers/codeql-syntax-check.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "codeql-syntax-check", - "pattern": [ - { - "regexp": "^((ERROR|WARNING): .* \\((.*):(\\d+),(\\d+)-\\d+\\))$", - "message": 1, - "file": 3, - "line": 4, - "col": 5, - "severity": 2 - } - ] - } - ] -} diff --git a/.github/problem-matchers/codeql-test-run.json b/.github/problem-matchers/codeql-test-run.json deleted file mode 100644 index 918758b33..000000000 --- a/.github/problem-matchers/codeql-test-run.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "codeql-test-run", - "pattern": [ - { - "regexp": "(\\[.*\\] FAILED\\((RESULT|COMPILATION)\\) (.*))$", - "file": 3, - "message": 1 - } - ] - } - ] -} diff --git a/.github/problem-matchers/make.json b/.github/problem-matchers/make.json deleted file mode 100644 index 8275f4851..000000000 --- a/.github/problem-matchers/make.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "make", - "pattern": [ - { - "regexp": "^(make: \\*\\*\\* .*)$", - "message": 1 - } - ] - } - ] -} diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml deleted file mode 100644 index d0467abb7..000000000 --- a/.github/workflows/check-change-note.yml +++ /dev/null @@ -1,33 +0,0 @@ -on: - pull_request_target: - types: [labeled, unlabeled, opened, synchronize, reopened, ready_for_review] - paths: - - "ql/src/**/*.ql" - - "ql/src/**/*.qll" - - "!**/experimental/**" - -jobs: - check-change-note: - runs-on: ubuntu-latest - steps: - - name: Check if change note file is present - uses: dorny/paths-filter@7c0f15b688b020e95e00f15c61299b022f08ca95 # v2.8.0 - id: paths_filter - with: - filters: | - change_note: - - '**/change-notes/*.md' - - name: Get PR labels - id: pr-labels - uses: joerick/pr-labels-action@0a4cc4ee0ab557ec0b1ae1157fa6fa7f9f4c494b # v1.0.6 - - name: Fail if change note is missing - uses: actions/github-script@v3 - if: | - github.event.pull_request.draft == false && - steps.paths_filter.outputs.change_note == 'false' && - !contains(steps.pr-labels.outputs.labels, ' no-change-note-required ') - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - core.setFailed('No change note found.' + - ' Either add one, or add the `no-change-note-required` label.') diff --git a/.github/workflows/codeqltest.yml b/.github/workflows/codeqltest.yml deleted file mode 100644 index 54e2e4d0a..000000000 --- a/.github/workflows/codeqltest.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: CodeQL tests -on: [pull_request] -jobs: - - test-linux: - name: Test Linux (Ubuntu) - runs-on: ubuntu-latest - steps: - - - name: Set up Go 1.18.1 - uses: actions/setup-go@v1 - with: - go-version: 1.18.1 - id: go - - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-linux64.zip - rm -f codeql-linux64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: Check out code - uses: actions/checkout@v2 - - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - - name: Build - run: env PATH=$PATH:$HOME/codeql make - - - name: Check that all QL and Go code is autoformatted - run: env PATH=$PATH:$HOME/codeql make check-formatting - - - name: Compile qhelp files to markdown - run: env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown - - - name: Upload qhelp markdown - uses: actions/upload-artifact@v2 - with: - name: qhelp-markdown - path: qhelp-out/**/*.md - - - name: Test - run: env PATH=$PATH:$HOME/codeql make test - - test-mac: - name: Test MacOS - runs-on: macOS-latest - steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v1 - with: - go-version: 1.18.1 - id: go - - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-osx64.zip - rm -f codeql-osx64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: Check out code - uses: actions/checkout@v2 - - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - - name: Build - run: env PATH=$PATH:$HOME/codeql make - - - name: Test - run: env PATH=$PATH:$HOME/codeql make test - - test-win: - name: Test Windows - runs-on: windows-2019 - steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v1 - with: - go-version: 1.18.1 - id: go - - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd "$HOME" - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q -o codeql-win64.zip - unzip -q -o codeql-win64.zip codeql/codeql.exe - rm -f codeql-win64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} - shell: - bash - - - name: Check out code - uses: actions/checkout@v2 - - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - - name: Build - run: | - $Env:Path += ";$HOME\codeql" - make - - - name: Test - run: | - $Env:Path += ";$HOME\codeql" - make test diff --git a/.gitignore b/.gitignore index 4fc12cb65..45b7f2501 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,3 @@ # editor and OS artifacts *~ .DS_STORE - -# query compilation caches -.cache - -# build artifacts -build/* - -# qltest projects and artifacts -ql/test/**/*.testproj -ql/test/**/*.actual -ql/test/**/go.sum - -# Java class files -**/*.class - -# binaries -tools/bin -tools/linux64 -tools/osx64 -tools/win64 -tools/tokenizer.jar -main - -# QL pack output directories -.codeql \ No newline at end of file diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index ec6d8765a..000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,5 +0,0 @@ -path_classifiers: - test: - - ql/test - example: - - ql/src diff --git a/Makefile b/Makefile deleted file mode 100644 index 7286f6871..000000000 --- a/Makefile +++ /dev/null @@ -1,142 +0,0 @@ -all: extractor ql/lib/go.dbscheme install-deps - -ifeq ($(OS),Windows_NT) -EXE = .exe -CODEQL_PLATFORM = win64 -else -EXE = -UNAME_S := $(shell uname -s) -ifeq ($(UNAME_S),Linux) -CODEQL_PLATFORM = linux64 -endif -ifeq ($(UNAME_S),Darwin) -CODEQL_PLATFORM = osx64 -endif -endif - -CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh pre-finalize.cmd pre-finalize.sh index.cmd index.sh linux64 osx64 win64 tracing-config.lua) - -EXTRACTOR_PACK_OUT = build/codeql-extractor-go - -BINARIES = go-extractor go-tokenizer go-autobuilder go-build-runner go-bootstrap go-gen-dbscheme - -.PHONY: tools tools-codeql tools-codeql-full clean autoformat \ - tools-linux64 tools-osx64 tools-win64 check-formatting - -clean: - rm -rf tools/bin tools/linux64 tools/osx64 tools/win64 tools/net tools/opencsv - rm -rf $(EXTRACTOR_PACK_OUT) build/stats build/testdb - -DATAFLOW_BRANCH=main - -autoformat: - find ql -iregex '.*\.qll?' -print0 | xargs -0 codeql query format -qq -i - find . -path '**/vendor' -prune -or -type f -iname '*.go' ! -empty -print0 | xargs -0 grep -L "//\s*autoformat-ignore" | xargs gofmt -w - -check-formatting: - find ql -iregex '.*\.qll?' -print0 | xargs -0 codeql query format --check-only - test -z "$$(find . -path '**/vendor' -prune -or -type f -iname '*.go' ! -empty -print0 | xargs -0 grep -L "//\s*autoformat-ignore" | xargs gofmt -l)" - -install-deps: - bash scripts/install-deps.sh $(CODEQL_LOCK_MODE) - -ifeq ($(QHELP_OUT_DIR),) -# If not otherwise specified, compile qhelp to markdown in place -QHELP_OUT_DIR := ql/src -endif - -qhelp-to-markdown: - scripts/qhelp-to-markdown.sh ql/src "$(QHELP_OUT_DIR)" - -tools: $(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES))) tools/tokenizer.jar - -.PHONY: $(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES))) -$(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES))): - go build -mod=vendor -o $@ ./extractor/cli/$(basename $(@F)) - -tools-codeql: tools-$(CODEQL_PLATFORM) - -tools-codeql-full: tools-linux64 tools-osx64 tools-win64 - -tools-linux64: $(addprefix tools/linux64/,$(BINARIES)) - -.PHONY: $(addprefix tools/linux64/,$(BINARIES)) -$(addprefix tools/linux64/,$(BINARIES)): - GOOS=linux GOARCH=amd64 go build -mod=vendor -o $@ ./extractor/cli/$(@F) - -tools-osx64: $(addprefix tools/osx64/,$(BINARIES)) - -.PHONY: $(addprefix tools/osx64/,$(BINARIES)) -$(addprefix tools/osx64/,$(BINARIES)): - GOOS=darwin GOARCH=amd64 go build -mod=vendor -o $@ ./extractor/cli/$(@F) - -tools-win64: $(addsuffix .exe,$(addprefix tools/win64/,$(BINARIES))) - -.PHONY: $(addsuffix .exe,$(addprefix tools/win64/,$(BINARIES))) -$(addsuffix .exe,$(addprefix tools/win64/,$(BINARIES))): - env GOOS=windows GOARCH=amd64 go build -mod=vendor -o $@ ./extractor/cli/$(basename $(@F)) - -.PHONY: extractor-common extractor extractor-full install-deps -extractor-common: codeql-extractor.yml LICENSE ql/lib/go.dbscheme \ - tools/tokenizer.jar $(CODEQL_TOOLS) - rm -rf $(EXTRACTOR_PACK_OUT) - mkdir -p $(EXTRACTOR_PACK_OUT) - cp codeql-extractor.yml LICENSE ql/lib/go.dbscheme ql/lib/go.dbscheme.stats $(EXTRACTOR_PACK_OUT) - mkdir $(EXTRACTOR_PACK_OUT)/tools - cp -r tools/tokenizer.jar $(CODEQL_TOOLS) $(EXTRACTOR_PACK_OUT)/tools - -extractor: extractor-common tools-codeql - cp -r tools/$(CODEQL_PLATFORM) $(EXTRACTOR_PACK_OUT)/tools - -extractor-full: extractor-common tools-codeql-full - cp -r $(addprefix tools/,linux64 osx64 win64) $(EXTRACTOR_PACK_OUT)/tools - -tools/tokenizer.jar: tools/net/sourceforge/pmd/cpd/GoLanguage.class - jar cf $@ -C tools net - jar uf $@ -C tools opencsv - -tools/net/sourceforge/pmd/cpd/GoLanguage.class: extractor/net/sourceforge/pmd/cpd/GoLanguage.java - javac -cp extractor -d tools $< - rm tools/net/sourceforge/pmd/cpd/AbstractLanguage.class - rm tools/net/sourceforge/pmd/cpd/SourceCode.class - rm tools/net/sourceforge/pmd/cpd/TokenEntry.class - rm tools/net/sourceforge/pmd/cpd/Tokenizer.class - -ql/lib/go.dbscheme: tools/$(CODEQL_PLATFORM)/go-gen-dbscheme$(EXE) - $< $@ - -build/stats/src.stamp: - mkdir -p $(@D)/src - git clone 'https://github.com/golang/tools' $(@D)/src - git -C $(@D)/src checkout 9b52d559c609 -q - touch $@ - -ql/lib/go.dbscheme.stats: ql/lib/go.dbscheme build/stats/src.stamp extractor - rm -rf build/stats/database - codeql database create -l go -s build/stats/src -j4 --search-path . build/stats/database - codeql dataset measure -o $@ build/stats/database/db-go - -test: all build/testdb/check-upgrade-path - codeql test run ql/test --search-path . --consistency-queries ql/test/consistency - # use GOOS=linux because GOOS=darwin GOARCH=386 is no longer supported - env GOOS=linux GOARCH=386 codeql$(EXE) test run ql/test/query-tests/Security/CWE-681 --search-path . --consistency-queries ql/test/consistency - cd extractor; go test -mod=vendor ./... | grep -vF "[no test files]" - bash extractor-smoke-test/test.sh || (echo "Extractor smoke test FAILED"; exit 1) - -.PHONY: build/testdb/check-upgrade-path -build/testdb/check-upgrade-path : build/testdb/go.dbscheme ql/lib/go.dbscheme - codeql dataset upgrade build/testdb --search-path ql/lib - diff -q build/testdb/go.dbscheme ql/lib/go.dbscheme - -.PHONY: build/testdb/go.dbscheme -build/testdb/go.dbscheme: ql/lib/upgrades/initial/go.dbscheme - rm -rf build/testdb - echo >build/empty.trap - codeql dataset import -S ql/lib/upgrades/initial/go.dbscheme build/testdb build/empty.trap - -.PHONY: sync-dataflow-libraries -sync-dataflow-libraries: - for f in DataFlowImpl.qll DataFlowImpl2.qll DataFlowImplCommon.qll DataFlowImplConsistency.qll tainttracking1/TaintTrackingImpl.qll tainttracking2/TaintTrackingImpl.qll FlowSummaryImpl.qll AccessPathSyntax.qll;\ - do\ - curl -s -o ./ql/lib/semmle/go/dataflow/internal/$$f https://raw.githubusercontent.com/github/codeql/$(DATAFLOW_BRANCH)/java/ql/lib/semmle/code/java/dataflow/internal/$$f;\ - done diff --git a/README.md b/README.md index 74a2039cd..d2b366803 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,15 @@ -# Go analysis support for CodeQL +# Relocation notice: `github/codeql-go` moved into `github/codeql` -This open-source repository contains the extractor, CodeQL libraries, and queries that power Go -support in [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) -makes available to its customers worldwide. +- Old location: The `github/codeql-go` repository, specifically https://github.com/github/codeql-go/releases/tag/archived +- New location: The `github/codeql` repository, specifically https://github.com/github/codeql/tree/main/go +- Detailed announcement: https://github.com/github/codeql-go/issues/741 -It contains two major components: - - an extractor, itself written in Go, that parses Go source code and converts it into a database - that can be queried using CodeQL. - - static analysis libraries and queries written in [CodeQL](https://codeql.github.com/docs/) that can be - used to analyze such a database to find coding mistakes or security vulnerabilities. +This repository previously contained the open-source CodeQL queries, libraries, and extractor for analyzing codebases written in the Go programming language with GitHub's CodeQL code analysis tools. These contents have been moved to the `github/codeql` repository, to live alongside similar libraries for all other programming languages supported by CodeQL. -The goal of this project is to provide comprehensive static analysis support for Go in CodeQL. +CodeQL support of the Go programming language remains the same, except that the code and the development will continue in `github/codeql`. -For the queries and libraries that power CodeQL support for other languages, visit [the CodeQL -repository](https://github.com/github/codeql). - -## Installation - -Clone this repository. - -Run `scripts/install-deps.sh`. This will ensure that the necessary external CodeQL packs are -downloaded to your machine. You will need to re-run this script whenever you pull new commits from -the repo. - -If you want to use the CodeQL extension for Visual Studio Code, import this repository into your VS -Code workspace. - -## Usage - -To analyze a Go codebase, either use the [CodeQL command-line -interface](https://codeql.github.com/docs/codeql-cli/) to create a database yourself, or -download a pre-built database from [LGTM.com](https://lgtm.com/). You can then run any of the -queries contained in this repository either on the command line or using the VS Code extension. - -Note that the [lgtm.com](https://github.com/github/codeql-go/tree/lgtm.com) branch of this -repository corresponds to the version of the queries that is currently deployed on LGTM.com. -The [main](https://github.com/github/codeql-go/tree/main) branch may contain changes that -have not been deployed yet, so you may need to upgrade databases downloaded from LGTM.com before -running queries on them. - -## Contributions - -Contributions are welcome! Please see our [contribution guidelines](CONTRIBUTING.md) and our -[code of conduct](CODE_OF_CONDUCT.md) for details on how to participate in our community. +Please visit https://github.com/github/codeql-go/issues/741 for more details about this relocation and how to update your CodeQL and code scanning workflows. ## Licensing The code in this repository is licensed under the [MIT license](LICENSE). - -## Resources - -- [Writing CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries/) -- [Learning CodeQL](https://codeql.github.com/docs/writing-codeql-queries/ql-tutorials/) diff --git a/alert_weighting.properties b/alert_weighting.properties deleted file mode 100644 index 0b7b5d90a..000000000 --- a/alert_weighting.properties +++ /dev/null @@ -1,3 +0,0 @@ -precision = ("veryhigh", "high", "medium", "low") -severity = ("error", "warning", "recommendation") -security = ("true", "false") diff --git a/build/.gitkeep b/build/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/codeql-extractor.yml b/codeql-extractor.yml deleted file mode 100644 index b258b193f..000000000 --- a/codeql-extractor.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: "go" -display_name: "Go" -version: 0.1.0 -pull_request_triggers: - - "**/go.mod" - - "**/glide.yaml" - - "**/Gopkg.toml" -column_kind: "utf8" -file_types: - - name: go - display_name: Go - extensions: - - .go -legacy_qltest_extraction: true diff --git a/codeql-tools/autobuild.cmd b/codeql-tools/autobuild.cmd deleted file mode 100644 index aed999876..000000000 --- a/codeql-tools/autobuild.cmd +++ /dev/null @@ -1,15 +0,0 @@ -@echo off -SETLOCAL EnableDelayedExpansion - -rem Some legacy environment variables for the autobuilder. -set LGTM_SRC=%CD% - -if "%CODEQL_EXTRACTOR_GO_BUILD_TRACING%"=="on" ( - echo "Tracing enabled" - type NUL && "%CODEQL_EXTRACTOR_GO_ROOT%/tools/%CODEQL_PLATFORM%/go-build-runner.exe" -) else ( - type NUL && "%CODEQL_EXTRACTOR_GO_ROOT%/tools/%CODEQL_PLATFORM%/go-autobuilder.exe" -) -exit /b %ERRORLEVEL% - -ENDLOCAL diff --git a/codeql-tools/autobuild.sh b/codeql-tools/autobuild.sh deleted file mode 100755 index 585152e67..000000000 --- a/codeql-tools/autobuild.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -set -eu - -if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; then - echo "Automatic build detection for $CODEQL_PLATFORM is not implemented." - exit 1 -fi - -# Some legacy environment variables used by the autobuilder. -LGTM_SRC="$(pwd)" -export LGTM_SRC - -if [ "${CODEQL_EXTRACTOR_GO_BUILD_TRACING:-}" = "on" ]; then - echo "Tracing enabled" - "$CODEQL_EXTRACTOR_GO_ROOT/tools/$CODEQL_PLATFORM/go-build-runner" -else - "$CODEQL_EXTRACTOR_GO_ROOT/tools/$CODEQL_PLATFORM/go-autobuilder" -fi diff --git a/codeql-tools/index.cmd b/codeql-tools/index.cmd deleted file mode 100644 index 21c8f64df..000000000 --- a/codeql-tools/index.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -SETLOCAL EnableDelayedExpansion - -type NUL && "%CODEQL_EXTRACTOR_GO_ROOT%/tools/%CODEQL_PLATFORM%/go-extractor.exe" -mod=vendor ./... -type NUL && "%CODEQL_EXTRACTOR_GO_ROOT%/tools/pre-finalize.cmd" -exit /b %ERRORLEVEL% - -ENDLOCAL diff --git a/codeql-tools/index.sh b/codeql-tools/index.sh deleted file mode 100755 index 877400d37..000000000 --- a/codeql-tools/index.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -set -eu - -if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; then - echo "Automatic build detection for $CODEQL_PLATFORM is not implemented." - exit 1 -fi - -"$CODEQL_EXTRACTOR_GO_ROOT/tools/$CODEQL_PLATFORM/go-extractor" -mod=vendor ./... -"$CODEQL_EXTRACTOR_GO_ROOT/tools/pre-finalize.sh" diff --git a/codeql-tools/linux64/compiler-tracing.spec b/codeql-tools/linux64/compiler-tracing.spec deleted file mode 100644 index 2055555c2..000000000 --- a/codeql-tools/linux64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder: - order compiler - trace no -**/go: - invoke ${config_dir}/go-extractor - prepend --mimic - prepend "${compiler}" diff --git a/codeql-tools/osx64/compiler-tracing.spec b/codeql-tools/osx64/compiler-tracing.spec deleted file mode 100644 index 2055555c2..000000000 --- a/codeql-tools/osx64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder: - order compiler - trace no -**/go: - invoke ${config_dir}/go-extractor - prepend --mimic - prepend "${compiler}" diff --git a/codeql-tools/pre-finalize.cmd b/codeql-tools/pre-finalize.cmd deleted file mode 100644 index 4abac2499..000000000 --- a/codeql-tools/pre-finalize.cmd +++ /dev/null @@ -1,19 +0,0 @@ -@echo off -SETLOCAL EnableDelayedExpansion - -if NOT "%CODEQL_EXTRACTOR_GO_EXTRACT_HTML%"=="no" ( - type NUL && "%CODEQL_DIST%/codeql.exe" database index-files ^ - --working-dir=. ^ - --include-extension=.htm ^ - --include-extension=.html ^ - --include-extension=.xhtm ^ - --include-extension=.xhtml ^ - --include-extension=.vue ^ - --size-limit 10m ^ - --language html ^ - -- ^ - "%CODEQL_EXTRACTOR_GO_WIP_DATABASE%" ^ - || echo "HTML extraction failed; continuing" - - exit /b %ERRORLEVEL% -) diff --git a/codeql-tools/pre-finalize.sh b/codeql-tools/pre-finalize.sh deleted file mode 100755 index 3a8b31c70..000000000 --- a/codeql-tools/pre-finalize.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -set -eu - -if [ "${CODEQL_EXTRACTOR_GO_EXTRACT_HTML:-yes}" != "no" ]; then - "$CODEQL_DIST/codeql" database index-files \ - --working-dir=. \ - --include-extension=.htm \ - --include-extension=.html \ - --include-extension=.xhtm \ - --include-extension=.xhtml \ - --include-extension=.vue \ - --size-limit 10m \ - --language html \ - -- \ - "$CODEQL_EXTRACTOR_GO_WIP_DATABASE" \ - || echo "HTML extraction failed; continuing." -fi diff --git a/codeql-tools/tracing-config.lua b/codeql-tools/tracing-config.lua deleted file mode 100644 index 8554d545c..000000000 --- a/codeql-tools/tracing-config.lua +++ /dev/null @@ -1,29 +0,0 @@ -function RegisterExtractorPack() - local goExtractor = GetPlatformToolsDirectory() .. 'go-extractor' - local patterns = { - CreatePatternMatcher({'^go-autobuilder$'}, MatchCompilerName, nil, - {trace = false}), - CreatePatternMatcher({'^go$'}, MatchCompilerName, goExtractor, { - prepend = {'--mimic', '${compiler}'}, - order = ORDER_BEFORE - }) - - } - if OperatingSystem == 'windows' then - goExtractor = goExtractor .. 'go-extractor.exe' - patterns = { - CreatePatternMatcher({'^go-autobuilder%.exe$'}, MatchCompilerName, - nil, {trace = false}), - CreatePatternMatcher({'^go%.exe$'}, MatchCompilerName, goExtractor, - { - prepend = {'--mimic', '"${compiler}"'}, - order = ORDER_BEFORE - }) - } - end - return patterns -end - --- Return a list of minimum supported versions of the configuration file format --- return one entry per supported major version. -function GetCompatibleVersions() return {'1.0.0'} end diff --git a/codeql-tools/win64/compiler-tracing.spec b/codeql-tools/win64/compiler-tracing.spec deleted file mode 100644 index 76a6b0114..000000000 --- a/codeql-tools/win64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder.exe: - order compiler - trace no -**/go.exe: - invoke ${config_dir}/go-extractor.exe - prepend --mimic - prepend "${compiler}" diff --git a/docs/language/learn-ql/go/ast-class-reference.rst b/docs/language/learn-ql/go/ast-class-reference.rst deleted file mode 100644 index d874652a8..000000000 --- a/docs/language/learn-ql/go/ast-class-reference.rst +++ /dev/null @@ -1,490 +0,0 @@ -Abstract syntax tree classes for working with Go programs -========================================================= - -CodeQL has a large selection of classes for representing the abstract syntax tree of Go programs. - -.. include:: ../../reusables/abstract-syntax-tree.rst - -Statement classes ------------------ - -This table lists all subclasses of `Stmt `__. - -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Statement syntax | CodeQL class | Superclasses | Remarks | -+===================================================================================================================+===================================================================================================================+===============================================================================================================+===================================================================================================================+ -| ``;`` | EmptyStmt_ | | | -| | | | | -| | .. _EmptyStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$EmptyStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ | ExprStmt_ | | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _ExprStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ExprStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``{`` Stmt_ ``...`` ``}`` | BlockStmt_ | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``if`` Expr_ BlockStmt_ | IfStmt_ | | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _IfStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$IfStmt.html | | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``if`` Expr_ BlockStmt_ ``else`` Stmt_ | | | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``if`` Stmt_\ ``;`` Expr_ BlockStmt_ | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``for`` Expr_ BlockStmt_ | ForStmt_ | LoopStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _ForStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ForStmt.html | .. _LoopStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$LoopStmt.html | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``for`` Stmt_\ ``;`` Expr_\ ``;`` Stmt_ BlockStmt_ | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``for`` Expr_ ``...`` ``=`` ``range`` Expr_ BlockStmt_ | RangeStmt_ | LoopStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _RangeStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$RangeStmt.html | .. _LoopStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$LoopStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _BlockStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BlockStmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``switch`` Expr_ ``{`` CaseClause_ ``...`` ``}`` | ExpressionSwitchStmt_ | SwitchStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _ExpressionSwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ExpressionSwitchStmt.html | .. _SwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SwitchStmt.html | | -| .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``switch`` Stmt_\ ``;`` Expr_ ``{`` CaseClause_ ``...`` ``}`` | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``switch`` Expr_\ ``.(type)`` ``{`` CaseClause_ ``...`` ``}`` | TypeSwitchStmt_ | SwitchStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _TypeSwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$TypeSwitchStmt.html | .. _SwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SwitchStmt.html | | -| .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``switch`` SimpleAssignStmt_\ ``.(type)`` ``{`` CaseClause_ ``...`` ``}`` | | | | -| | | | | -| .. _SimpleAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SimpleAssignStmt.html | | | | -| .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``switch`` Stmt_\ ``;`` Expr_\ ``.(type)`` ``{`` CaseClause_ ``...`` ``}`` | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -| .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``select`` ``{`` CommClause_ ``...`` ``}`` | SelectStmt_ | | | -| | | | | -| .. _CommClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CommClause.html | .. _SelectStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SelectStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``return`` | ReturnStmt_ | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``return`` Expr_ ``...`` | .. _ReturnStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ReturnStmt.html | | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``break`` | BreakStmt_ | BranchStmt_ | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``break`` LabelName_ | .. _BreakStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BreakStmt.html | .. _BranchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BranchStmt.html | | -| | | | | -| .. _LabelName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$LabelName.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``continue`` | ContinueStmt_ | BranchStmt_ | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``continue`` LabelName_ | .. _ContinueStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ContinueStmt.html | .. _BranchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BranchStmt.html | | -| | | | | -| .. _LabelName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$LabelName.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``goto`` LabelName_ | GotoStmt_ | BranchStmt_ | | -| | | | | -| .. _LabelName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$LabelName.html | .. _GotoStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$GotoStmt.html | .. _BranchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BranchStmt.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``fallthrough`` | FallthroughStmt_ | BranchStmt_ | can only occur as final non-empty child of a CaseClause_ in an ExpressionSwitchStmt_ | -| | | | | -| | .. _FallthroughStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$FallthroughStmt.html | .. _BranchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BranchStmt.html | .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | -| | | | .. _ExpressionSwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ExpressionSwitchStmt.html | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| LabelName_\ ``:`` Stmt_ | LabeledStmt_ | | | -| | | | | -| .. _LabelName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$LabelName.html | .. _LabeledStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$LabeledStmt.html | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``var`` VariableName_ TypeName_ | DeclStmt_ | | | -| | | | | -| .. _VariableName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$VariableName.html | .. _DeclStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$DeclStmt.html | | | -| .. _TypeName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeName.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``const`` VariableName_ ``=`` Expr_ | | | | -| | | | | -| .. _VariableName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$VariableName.html | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``type`` TypeName_ TypeExpr_ | | | | -| | | | | -| .. _TypeName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeName.html | | | | -| .. _TypeExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeExpr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``type`` TypeName_ ``=`` TypeExpr_ | | | | -| | | | | -| .. _TypeName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeName.html | | | | -| .. _TypeExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeExpr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``...`` ``=`` Expr_ ``...`` | AssignStmt_ | SimpleAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _AssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$AssignStmt.html | .. _SimpleAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SimpleAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| VariableName_ ``...`` ``:=`` Expr_ ``...`` | DefineStmt_ | SimpleAssignStmt_, Assignment_ | | -| | | | | -| .. _VariableName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$VariableName.html | .. _DefineStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$DefineStmt.html | .. _SimpleAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SimpleAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``+=`` Expr_ | AddAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _AddAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$AddAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``-=`` Expr_ | SubAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _SubAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SubAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``*=`` Expr_ | MulAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _MulAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$MulAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``/=`` Expr_ | QuoAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _QuoAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$QuoAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``%=`` Expr_ | RemAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _RemAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$RemAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``*=`` Expr_ | MulAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _MulAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$MulAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``&=`` Expr_ | AndAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _AndAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$AndAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``|=`` Expr_ | OrAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _OrAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$OrAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``^=`` Expr_ | XorAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _XorAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$XorAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``<<=`` Expr_ | ShlAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _ShlAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ShlAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``>>=`` Expr_ | ShrAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _ShrAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$ShrAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``&^=`` Expr_ | AndNotAssignStmt_ | CompoundAssignStmt_, Assignment_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _AndNotAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$AndNotAssignStmt.html | .. _CompoundAssignStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CompoundAssignStmt.html | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | .. _Assignment: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Assignment.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``++`` | IncStmt_ | IncDecStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _IncStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$IncStmt.html | .. _IncDecStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$IncDecStmt.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``--`` | DecStmt_ | IncDecStmt_ | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _DecStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$DecStmt.html | .. _IncDecStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$IncDecStmt.html | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``go`` CallExpr_ | GoStmt_ | | | -| | | | | -| .. _CallExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$CallExpr.html | .. _GoStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$GoStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``defer`` CallExpr_ | DeferStmt_ | | | -| | | | | -| .. _CallExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$CallExpr.html | .. _DeferStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$DeferStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``<-`` Expr_ | SendStmt_ | | | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _SendStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SendStmt.html | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``case`` Expr_ ``...``\ ``:`` Stmt_ ``...`` | CaseClause_ | | can only occur as child of a SwitchStmt_ | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _CaseClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CaseClause.html | | .. _SwitchStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SwitchStmt.html | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``case`` TypeExpr_ ``...``\ ``:`` Stmt_ ``...`` | | | | -| | | | | -| .. _TypeExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$TypeExpr.html | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``default:`` Stmt_ ``...`` | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| ``case`` SendStmt_\ ``:`` Stmt_ ``...`` | CommClause_ | | can only occur as child of a SelectStmt_ | -| | | | | -| .. _SendStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SendStmt.html | .. _CommClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CommClause.html | | .. _SelectStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$SelectStmt.html | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``case`` RecvStmt_\ ``:`` Stmt_ ``...`` | | | | -| | | | | -| .. _RecvStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$RecvStmt.html | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| ``default:`` Stmt_ ``...`` | | | | -| | | | | -| .. _Stmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$Stmt.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| Expr_ ``...`` ``=`` RecvExpr_ | RecvStmt_ | | can only occur as child of a CommClause_ | -| | | | | -| .. _Expr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$Expr.html | .. _RecvStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$RecvStmt.html | | .. _CommClause: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$CommClause.html | -| .. _RecvExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$RecvExpr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+ | | | -| VariableName_ ``...`` ``:=`` RecvExpr_ | | | | -| | | | | -| .. _VariableName: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$VariableName.html | | | | -| .. _RecvExpr: https://help.semmle.com/qldoc/go/semmle/go/Expr.qll/type.Expr$RecvExpr.html | | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ -| (anything unparseable) | BadStmt_ | | | -| | | | | -| | .. _BadStmt: https://help.semmle.com/qldoc/go/semmle/go/Stmt.qll/type.Stmt$BadStmt.html | | | -+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+ - -Expression classes ------------------- - -There are many expression classes, so we present them by category. -All classes in this section are subclasses of -`Expr `__. - -Literals -~~~~~~~~ - -All classes in this subsection are subclasses of -`Literal `__. - -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Expression syntax example | CodeQL class | Superclass | -+=========================================+==============================================================================================+==============================================================================================================================================================================================================+ -| ``23`` | `IntLit `__ | `BasicLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``4.2`` | `FloatLit `__ | `BasicLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``4.2 + 2.7i`` | `ImagLit `__ | `BasicLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``'a'`` | `CharLit `__ | `BasicLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``"Hello"`` | `StringLit `__ | `BasicLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``func(x, y int) int { return x + y }`` | `FuncLit `__ | `FuncDef `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``[6]int{1, 2, 3, 5}`` | `ArrayLit `__ | `ArrayOrSliceLit `__, `CompositeLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``[]int{1, 2, 3, 5}`` | `SliceLit `__ | `ArrayOrSliceLit `__, `CompositeLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``map[string]int{"A": 1, "B": 2}`` | `MapLit `__ | `CompositeLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``Point3D{0.5, -0.5, 0.5}`` | `StructLit `__ | `CompositeLit `__ | -+-----------------------------------------+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -Unary expressions -~~~~~~~~~~~~~~~~~ - -All classes in this subsection are subclasses of -`UnaryExpr `__. - -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| Expression syntax | CodeQL class | Superclasses | -+============================================================================================+========================================================================================================+==================================================================================================================+ -| ``+``\ `Expr `__ | `PlusExpr `__ | `ArithmeticUnaryExpr `__ | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``-``\ `Expr `__ | `MinusExpr `__ | `ArithmeticUnaryExpr `__ | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``!``\ `Expr `__ | `NotExpr `__ | `LogicalUnaryExpr `__ | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``^``\ `Expr `__ | `ComplementExpr `__ | `BitwiseUnaryExpr `__ | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``&``\ `Expr `__ | `AddressExpr `__ | | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``<-``\ `Expr `__ | `RecvExpr `__ | | -+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------+ - -Binary expressions -~~~~~~~~~~~~~~~~~~ - -All classes in this subsection are subclasses of -`BinaryExpr `__. - -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Expression syntax | CodeQL class | Superclasses | -+==============================================================================================================================================================================+================================================================================================+============================================================================================================================+ -| `Expr `__ ``*`` `Expr `__ | `MulExpr `__ | `ArithmeticBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``/`` `Expr `__ | `QuoExpr `__ | `ArithmeticBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``%`` `Expr `__ | `RemExpr `__ | `ArithmeticBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``+`` `Expr `__ | `AddExpr `__ | `ArithmeticBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``-`` `Expr `__ | `SubExpr `__ | `ArithmeticBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``<<`` `Expr `__ | `ShlExpr `__ | `ShiftExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``>>`` `Expr `__ | `ShrExpr `__ | `ShiftExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``&&`` `Expr `__ | `LandExpr `__ | `LogicalBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``||`` `Expr `__ | `LorExpr `__ | `LogicalBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``<`` `Expr `__ | `LssExpr `__ | `RelationalComparisonExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``>`` `Expr `__ | `GtrExpr `__ | `RelationalComparisonExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``<=`` `Expr `__ | `LeqExpr `__ | `RelationalComparisonExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``>=`` `Expr `__ | `GeqExpr `__ | `RelationalComparisonExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``==`` `Expr `__ | `EqlExpr `__ | `EqualityTestExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``!=`` `Expr `__ | `NeqExpr `__ | `EqualityTestExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``&`` `Expr `__ | `AndExpr `__ | `BitwiseBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``|`` `Expr `__ | `OrExpr `__ | `BitwiseBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``^`` `Expr `__ | `XorExpr `__ | `BitwiseBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__ ``&^`` `Expr `__ | `AndNotExpr `__ | `BitwiseBinaryExpr `__ | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+ - -Type expressions -~~~~~~~~~~~~~~~~ - -All classes in this subsection are subclasses of -`TypeExpr `__. - -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| Expression syntax | CodeQL class | Superclasses | -+=========================================================================================================================================================================================================+====================================================================================================================+====================================================================================================+ -| ``[``\ `Expr `__\ ``]`` `TypeExpr `__ | `ArrayTypeExpr `__ | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``struct { ... }`` | `StructTypeExpr `__ | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``func`` `FunctionName `__\ ``(...) (...)`` | `FuncTypeExpr `__ | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``interface { ... }`` | `InterfaceTypeExpr `__ | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``map[``\ `TypeExpr `__\ ``]``\ `TypeExpr `__ | `MapTypeExpr `__ | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``chan<-`` `TypeExpr `__ | `SendChanTypeExpr `__ | `ChanTypeExpr `__ | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``<-chan`` `TypeExpr `__ | `RecvChanTypeExpr `__ | `ChanTypeExpr `__ | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| ``chan`` `TypeExpr `__ | `SendRecvChanTypeExpr `__ | `ChanTypeExpr `__ | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ - -Name expressions -~~~~~~~~~~~~~~~~ - -All classes in this subsection are subclasses of -`Name `__. - -The following classes relate to the structure of the name. - -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| Expression syntax | CodeQL class | Superclasses | -+===================================================================================================================================================================================+======================================================================================================+====================================================================================================+ -| `Ident `__ | `SimpleName `__ | `Ident `__ | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| `Ident `__\ ``.``\ `Ident `__ | `QualifiedName `__ | `SelectorExpr `__ | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ - -The following classes relate to what sort of entity the name refers to. - - -- `PackageName `__ -- `TypeName `__ -- `LabelName `__ -- `ValueName `__ - - - `ConstantName `__ - - `VariableName `__ - - `FunctionName `__ - -Miscellaneous -~~~~~~~~~~~~~ - -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Expression syntax | CodeQL class | Superclasses | Remarks | -+============================================================================================================================================================================================================================================================================================================================================================================+========================================================================================================+====================================================================================================================+==========================================================================================================================================================================================================================+ -| ``foo`` | `Ident `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``_`` | `BlankIdent `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``...`` | `Ellipsis `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``(``\ `Expr `__\ ``)`` | `ParenExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Ident `__\ ``.``\ `Ident `__ | `SelectorExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__\ ``[``\ `Expr `__\ ``]`` | `IndexExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__\ ``[``\ `Expr `__\ ``:``\ `Expr `__\ ``:``\ `Expr `__\ ``]`` | `SliceExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__\ ``.(``\ `TypeExpr `__\ ``)`` | `TypeAssertExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``*``\ `Expr `__ | `StarExpr `__ | | can be a `ValueExpr `__ or `TypeExpr `__ depending on context | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__\ ``:`` `Expr `__ | `KeyValueExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `TypeExpr `__\ ``(``\ `Expr `__\ ``)`` | `ConversionExpr `__ | `CallOrConversionExpr `__ | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Expr `__\ ``(...)`` | `CallExpr `__ | `CallOrConversionExpr `__ | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| (anything unparseable) | `BadExpr `__ | | | -+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -The following classes organize expressions by the kind of entity they refer to. - -+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| CodeQL class | Explanation | -+======================================================================================================+=========================================================================================================================================================================================================================================================+ -| `TypeExpr `__ | an expression that denotes a type | -+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `ReferenceExpr `__ | an expression that refers to a variable, a constant, a function, a field, or an element of an array or a slice | -+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `ValueExpr `__ | an expression that can be evaluated to a value (as opposed to expressions that refer to a package, a type, or a statement label). This generalizes `ReferenceExpr `__ | -+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -Further reading ---------------- - -.. include:: ../../reusables/go-further-reading.rst -.. include:: ../../reusables/codeql-ref-tools-further-reading.rst \ No newline at end of file diff --git a/docs/language/learn-ql/go/library-modeling-go.rst b/docs/language/learn-ql/go/library-modeling-go.rst deleted file mode 100644 index 159f4c997..000000000 --- a/docs/language/learn-ql/go/library-modeling-go.rst +++ /dev/null @@ -1,122 +0,0 @@ -Modeling data flow in Go libraries -================================== - -When analyzing a Go program, CodeQL does not examine the source code for -external packages. To track the flow of untrusted data through a library, you -can create a model of the library. - -You can find existing models in the ``ql/src/semmle/go/frameworks/`` folder of the -`CodeQL for Go repository `__. -To add a new model, you should make a new file in that folder, named after the library. - -Sources -------- - -To mark a source of data that is controlled by an untrusted user, we -create a class extending ``UntrustedFlowSource::Range``. Inheritance and -the characteristic predicate of the class should be used to specify -exactly the dataflow node that introduces the data. Here is a short -example from ``Mux.qll``. - -.. code-block:: ql - - class RequestVars extends DataFlow::UntrustedFlowSource::Range, DataFlow::CallNode { - RequestVars() { this.getTarget().hasQualifiedName("github.com/gorilla/mux", "Vars") } - } - -This has the effect that all calls to `the function Vars from the -package mux `__ are -treated as sources of untrusted data. - -Flow propagation ----------------- - -By default, we assume that all functions in libraries do not have -any data flow. To indicate that a particular function does have data flow, -create a class extending ``TaintTracking::FunctionModel`` (or -``DataFlow::FunctionModel`` if the untrusted user data is passed on -without being modified). - -Inheritance and the characteristic predicate of the class should specify -the function. The class should also have a member predicate with the signature -``override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp)`` -(or -``override predicate hasDataFlow(FunctionInput inp, FunctionOutput outp)`` -if extending ``DataFlow::FunctionModel``). The body should constrain -``inp`` and ``outp``. - -``FunctionInput`` is an abstract representation of the inputs to a -function. The options are: - -* the receiver (``inp.isReceiver()``) -* one of the parameters (``inp.isParameter(i)``) -* one of the results (``inp.isResult(i)``, or ``inp.isResult`` if there is only one result) - -Note that it may seem strange that the result of a function could be -considered as a function input, but it is needed in some cases. For -instance, the function ``bufio.NewWriter`` returns a writer ``bw`` that -buffers write operations to an underlying writer ``w``. If tainted data -is written to ``bw``, then it makes sense to propagate that taint back -to the underlying writer ``w``, which can be modeled by saying that -``bufio.NewWriter`` propagates taint from its result to its first -argument. - -Similarly, ``FunctionOutput`` is an abstract representation of the -outputs to a function. The options are: - -* the receiver (``outp.isReceiver()``) -* one of the parameters (``outp.isParameter(i)``) -* one of the results (``outp.isResult(i)``, or ``outp.isResult`` if there is only one result) - -Here is an example from ``Gin.qll``, which has been slightly simplified. - -.. code-block:: ql - - private class ParamsGet extends TaintTracking::FunctionModel, Method { - ParamsGet() { this.hasQualifiedName("github.com/gin-gonic/gin", "Params", "Get") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult(0) - } - } - -This has the effect that calls to the ``Get`` method with receiver type -``Params`` from the ``gin-gonic/gin`` package allow taint to flow from -the receiver to the first result. In other words, if ``p`` has type -``Params`` and taint can flow to it, then after the line -``x := p.Get("foo")`` taint can also flow to ``x``. - -Sanitizers ----------- - -It is not necessary to indicate that library functions are sanitizers. -Their bodies are not analyzed, so it is assumed that data does not -flow through them. - -Sinks ------ - -Data-flow sinks are specified by queries rather than by library models. -However, you can use library models to indicate when functions belong to -special categories. Queries can then use these categories when specifying -sinks. Classes representing these special categories are contained in -``ql/src/semmle/go/Concepts.qll`` in the `CodeQL for Go repository -`__. -``Concepts.qll`` includes classes for logger mechanisms, -HTTP response writers, HTTP redirects, and marshaling and unmarshaling -functions. - -Here is a short example from ``Stdlib.qll``, which has been slightly simplified. - -.. code-block:: ql - - private class PrintfCall extends LoggerCall::Range, DataFlow::CallNode { - PrintfCall() { this.getTarget().hasQualifiedName("fmt", ["Print", "Printf", "Println"]) } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - -This has the effect that any call to ``Print``, ``Printf``, or -``Println`` in the package ``fmt`` is recognized as a logger call. -Any query that uses logger calls as a sink will then identify when tainted data -has been passed as an argument to ``Print``, ``Printf``, or ``Println``. \ No newline at end of file diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql deleted file mode 100644 index b4b7498e2..000000000 --- a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql +++ /dev/null @@ -1,41 +0,0 @@ -class Expr_ extends @expr { - string toString() { result = "Expr" } -} - -class ExprParent_ extends @exprparent { - string toString() { result = "ExprParent" } -} - -/** - * Two new kinds have been inserted such that `@sliceexpr` which used to have - * index 13 now has index 15. Another new kind has been inserted such that - * `@plusexpr` which used to have index 23 now has index 26. Entries with - * indices lower than 13 are unchanged. - */ -bindingset[new_index] -int old_index(int new_index) { - if new_index < 13 - then result = new_index - else - if new_index = [13, 14] - then result = 0 // badexpr - else - if new_index < 23 - then result + (15 - 13) = new_index - else - if new_index = 23 - then result = 0 // badexpr - else result + (26 - 23) = new_index -} - -// The schema for exprs is: -// -// exprs(unique int id: @expr, -// int kind: int ref, -// int parent: @exprparent ref, -// int idx: int ref); -from Expr_ expr, int new_kind, ExprParent_ parent, int idx, int old_kind -where - exprs(expr, new_kind, parent, idx) and - old_kind = old_index(new_kind) -select expr, old_kind, parent, idx diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme deleted file mode 100644 index 8f168c8af..000000000 --- a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme +++ /dev/null @@ -1,531 +0,0 @@ -/** Auto-generated dbscheme; do not edit. */ - - -/** Duplicate code **/ - -duplicateCode( - unique int id : @duplication, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -similarCode( - unique int id : @similarity, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -@duplication_or_similarity = @duplication | @similarity; - -tokens( - int id : @duplication_or_similarity ref, - int offset : int ref, - int beginLine : int ref, - int beginColumn : int ref, - int endLine : int ref, - int endColumn : int ref); - -/** External data **/ - -externalData( - int id : @externalDataElement, - varchar(900) path : string ref, - int column: int ref, - varchar(900) value : string ref -); - -snapshotDate(unique date snapshotDate : date ref); - -sourceLocationPrefix(varchar(900) prefix : string ref); - - -/* - * XML Files - */ - -xmlEncoding( - unique int id: @file ref, - string encoding: string ref -); - -xmlDTDs( - unique int id: @xmldtd, - string root: string ref, - string publicId: string ref, - string systemId: string ref, - int fileid: @file ref -); - -xmlElements( - unique int id: @xmlelement, - string name: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int fileid: @file ref -); - -xmlAttrs( - unique int id: @xmlattribute, - int elementid: @xmlelement ref, - string name: string ref, - string value: string ref, - int idx: int ref, - int fileid: @file ref -); - -xmlNs( - int id: @xmlnamespace, - string prefixName: string ref, - string URI: string ref, - int fileid: @file ref -); - -xmlHasNs( - int elementId: @xmlnamespaceable ref, - int nsId: @xmlnamespace ref, - int fileid: @file ref -); - -xmlComments( - unique int id: @xmlcomment, - string text: string ref, - int parentid: @xmlparent ref, - int fileid: @file ref -); - -xmlChars( - unique int id: @xmlcharacters, - string text: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int isCDATA: int ref, - int fileid: @file ref -); - -@xmlparent = @file | @xmlelement; -@xmlnamespaceable = @xmlelement | @xmlattribute; - -xmllocations( - int xmlElement: @xmllocatable ref, - int location: @location_default ref -); - -@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; - -compilations(unique int id: @compilation, string cwd: string ref); - -#keyset[id, num] -compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); - -#keyset[id, num, kind] -compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); - -diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); - -compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); - -#keyset[id, num] -compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); - -diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, - string full_error_message: string ref, int location: @location ref); - -locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, - int endLine: int ref, int endColumn: int ref); - -numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); - -files(unique int id: @file, string name: string ref); - -folders(unique int id: @folder, string name: string ref); - -containerparent(int parent: @container ref, unique int child: @container ref); - -has_location(unique int locatable: @locatable ref, int location: @location ref); - -#keyset[parent, idx] -comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); - -comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); - -doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); - -#keyset[parent, idx] -exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); - -literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); - -constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); - -fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); - -#keyset[parent, idx] -stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); - -#keyset[parent, idx] -decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); - -#keyset[parent, idx] -specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); - -scopes(unique int id: @scope, int kind: int ref); - -scopenesting(unique int inner: @scope ref, int outer: @scope ref); - -scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); - -objects(unique int id: @object, int kind: int ref, string name: string ref); - -objectscopes(unique int object: @object ref, int scope: @scope ref); - -objecttypes(unique int object: @object ref, int tp: @type ref); - -methodreceivers(unique int method: @object ref, int receiver: @object ref); - -fieldstructs(unique int field: @object ref, int struct: @structtype ref); - -methodhosts(int method: @object ref, int host: @namedtype ref); - -defs(int ident: @ident ref, int object: @object ref); - -uses(int ident: @ident ref, int object: @object ref); - -types(unique int id: @type, int kind: int ref); - -type_of(unique int expr: @expr ref, int tp: @type ref); - -typename(unique int tp: @type ref, string name: string ref); - -key_type(unique int map: @maptype ref, int tp: @type ref); - -element_type(unique int container: @containertype ref, int tp: @type ref); - -base_type(unique int ptr: @pointertype ref, int tp: @type ref); - -underlying_type(unique int named: @namedtype ref, int tp: @type ref); - -#keyset[parent, index] -component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); - -array_length(unique int tp: @arraytype ref, string len: string ref); - -type_objects(unique int tp: @type ref, int object: @object ref); - -packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); - -#keyset[parent, idx] -modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); - -#keyset[parent, idx] -modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); - -#keyset[package, idx] -errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, - string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); - -has_ellipsis(int id: @callorconversionexpr ref); - -variadic(int id: @signaturetype ref); - -@container = @file | @folder; - -@locatable = @xmllocatable | @node | @localscope; - -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode - | @comment_group | @comment; - -@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; - -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; - -@modexprparent = @file | @modexpr; - -@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; - -@stmtparent = @funcdef | @stmt | @decl; - -@declparent = @file | @declstmt; - -@funcdef = @funclit | @funcdecl; - -@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; - -@location = @location_default; - -@sourceline = @locatable; - -case @comment.kind of - 0 = @slashslashcomment -| 1 = @slashstarcomment; - -case @expr.kind of - 0 = @badexpr -| 1 = @ident -| 2 = @ellipsis -| 3 = @intlit -| 4 = @floatlit -| 5 = @imaglit -| 6 = @charlit -| 7 = @stringlit -| 8 = @funclit -| 9 = @compositelit -| 10 = @parenexpr -| 11 = @selectorexpr -| 12 = @indexexpr -| 13 = @sliceexpr -| 14 = @typeassertexpr -| 15 = @callorconversionexpr -| 16 = @starexpr -| 17 = @keyvalueexpr -| 18 = @arraytypeexpr -| 19 = @structtypeexpr -| 20 = @functypeexpr -| 21 = @interfacetypeexpr -| 22 = @maptypeexpr -| 23 = @plusexpr -| 24 = @minusexpr -| 25 = @notexpr -| 26 = @complementexpr -| 27 = @derefexpr -| 28 = @addressexpr -| 29 = @arrowexpr -| 30 = @lorexpr -| 31 = @landexpr -| 32 = @eqlexpr -| 33 = @neqexpr -| 34 = @lssexpr -| 35 = @leqexpr -| 36 = @gtrexpr -| 37 = @geqexpr -| 38 = @addexpr -| 39 = @subexpr -| 40 = @orexpr -| 41 = @xorexpr -| 42 = @mulexpr -| 43 = @quoexpr -| 44 = @remexpr -| 45 = @shlexpr -| 46 = @shrexpr -| 47 = @andexpr -| 48 = @andnotexpr -| 49 = @sendchantypeexpr -| 50 = @recvchantypeexpr -| 51 = @sendrcvchantypeexpr -| 52 = @errorexpr; - -@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; - -@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; - -@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; - -@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; - -@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; - -@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; - -@logicalunaryexpr = @notexpr; - -@bitwiseunaryexpr = @complementexpr; - -@arithmeticunaryexpr = @plusexpr | @minusexpr; - -@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; - -@logicalbinaryexpr = @lorexpr | @landexpr; - -@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; - -@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; - -@shiftexpr = @shlexpr | @shrexpr; - -@comparison = @equalitytest | @relationalcomparison; - -@equalitytest = @eqlexpr | @neqexpr; - -@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; - -@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; - -case @stmt.kind of - 0 = @badstmt -| 1 = @declstmt -| 2 = @emptystmt -| 3 = @labeledstmt -| 4 = @exprstmt -| 5 = @sendstmt -| 6 = @incstmt -| 7 = @decstmt -| 8 = @gostmt -| 9 = @deferstmt -| 10 = @returnstmt -| 11 = @breakstmt -| 12 = @continuestmt -| 13 = @gotostmt -| 14 = @fallthroughstmt -| 15 = @blockstmt -| 16 = @ifstmt -| 17 = @caseclause -| 18 = @exprswitchstmt -| 19 = @typeswitchstmt -| 20 = @commclause -| 21 = @selectstmt -| 22 = @forstmt -| 23 = @rangestmt -| 24 = @assignstmt -| 25 = @definestmt -| 26 = @addassignstmt -| 27 = @subassignstmt -| 28 = @mulassignstmt -| 29 = @quoassignstmt -| 30 = @remassignstmt -| 31 = @andassignstmt -| 32 = @orassignstmt -| 33 = @xorassignstmt -| 34 = @shlassignstmt -| 35 = @shrassignstmt -| 36 = @andnotassignstmt; - -@incdecstmt = @incstmt | @decstmt; - -@assignment = @simpleassignstmt | @compoundassignstmt; - -@simpleassignstmt = @assignstmt | @definestmt; - -@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt - | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; - -@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; - -@switchstmt = @exprswitchstmt | @typeswitchstmt; - -@loopstmt = @forstmt | @rangestmt; - -case @decl.kind of - 0 = @baddecl -| 1 = @importdecl -| 2 = @constdecl -| 3 = @typedecl -| 4 = @vardecl -| 5 = @funcdecl; - -@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; - -case @spec.kind of - 0 = @importspec -| 1 = @valuespec -| 2 = @typedefspec -| 3 = @aliasspec; - -@typespec = @typedefspec | @aliasspec; - -case @object.kind of - 0 = @pkgobject -| 1 = @decltypeobject -| 2 = @builtintypeobject -| 3 = @declconstobject -| 4 = @builtinconstobject -| 5 = @declvarobject -| 6 = @declfunctionobject -| 7 = @builtinfunctionobject -| 8 = @labelobject; - -@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; - -@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; - -@typeobject = @decltypeobject | @builtintypeobject; - -@valueobject = @constobject | @varobject | @functionobject; - -@constobject = @declconstobject | @builtinconstobject; - -@varobject = @declvarobject; - -@functionobject = @declfunctionobject | @builtinfunctionobject; - -case @scope.kind of - 0 = @universescope -| 1 = @packagescope -| 2 = @localscope; - -case @type.kind of - 0 = @invalidtype -| 1 = @boolexprtype -| 2 = @inttype -| 3 = @int8type -| 4 = @int16type -| 5 = @int32type -| 6 = @int64type -| 7 = @uinttype -| 8 = @uint8type -| 9 = @uint16type -| 10 = @uint32type -| 11 = @uint64type -| 12 = @uintptrtype -| 13 = @float32type -| 14 = @float64type -| 15 = @complex64type -| 16 = @complex128type -| 17 = @stringexprtype -| 18 = @unsafepointertype -| 19 = @boolliteraltype -| 20 = @intliteraltype -| 21 = @runeliteraltype -| 22 = @floatliteraltype -| 23 = @complexliteraltype -| 24 = @stringliteraltype -| 25 = @nilliteraltype -| 26 = @arraytype -| 27 = @slicetype -| 28 = @structtype -| 29 = @pointertype -| 30 = @interfacetype -| 31 = @tupletype -| 32 = @signaturetype -| 33 = @maptype -| 34 = @sendchantype -| 35 = @recvchantype -| 36 = @sendrcvchantype -| 37 = @namedtype; - -@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; - -@booltype = @boolexprtype | @boolliteraltype; - -@numerictype = @integertype | @floattype | @complextype; - -@integertype = @signedintegertype | @unsignedintegertype; - -@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; - -@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; - -@floattype = @float32type | @float64type | @floatliteraltype; - -@complextype = @complex64type | @complex128type | @complexliteraltype; - -@stringtype = @stringexprtype | @stringliteraltype; - -@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype - | @stringliteraltype | @nilliteraltype; - -@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; - -@containertype = @arraytype | @slicetype | @maptype | @chantype; - -@chantype = @sendchantype | @recvchantype | @sendrcvchantype; - -case @modexpr.kind of - 0 = @modcommentblock -| 1 = @modline -| 2 = @modlineblock -| 3 = @modlparen -| 4 = @modrparen; - -case @error.kind of - 0 = @unknownerror -| 1 = @listerror -| 2 = @parseerror -| 3 = @typeerror; - diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme deleted file mode 100644 index 90fa7836e..000000000 --- a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme +++ /dev/null @@ -1,547 +0,0 @@ -/** Auto-generated dbscheme; do not edit. */ - - -/** Duplicate code **/ - -duplicateCode( - unique int id : @duplication, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -similarCode( - unique int id : @similarity, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -@duplication_or_similarity = @duplication | @similarity; - -tokens( - int id : @duplication_or_similarity ref, - int offset : int ref, - int beginLine : int ref, - int beginColumn : int ref, - int endLine : int ref, - int endColumn : int ref); - -/** External data **/ - -externalData( - int id : @externalDataElement, - varchar(900) path : string ref, - int column: int ref, - varchar(900) value : string ref -); - -snapshotDate(unique date snapshotDate : date ref); - -sourceLocationPrefix(varchar(900) prefix : string ref); - - -/* - * XML Files - */ - -xmlEncoding( - unique int id: @file ref, - string encoding: string ref -); - -xmlDTDs( - unique int id: @xmldtd, - string root: string ref, - string publicId: string ref, - string systemId: string ref, - int fileid: @file ref -); - -xmlElements( - unique int id: @xmlelement, - string name: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int fileid: @file ref -); - -xmlAttrs( - unique int id: @xmlattribute, - int elementid: @xmlelement ref, - string name: string ref, - string value: string ref, - int idx: int ref, - int fileid: @file ref -); - -xmlNs( - int id: @xmlnamespace, - string prefixName: string ref, - string URI: string ref, - int fileid: @file ref -); - -xmlHasNs( - int elementId: @xmlnamespaceable ref, - int nsId: @xmlnamespace ref, - int fileid: @file ref -); - -xmlComments( - unique int id: @xmlcomment, - string text: string ref, - int parentid: @xmlparent ref, - int fileid: @file ref -); - -xmlChars( - unique int id: @xmlcharacters, - string text: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int isCDATA: int ref, - int fileid: @file ref -); - -@xmlparent = @file | @xmlelement; -@xmlnamespaceable = @xmlelement | @xmlattribute; - -xmllocations( - int xmlElement: @xmllocatable ref, - int location: @location_default ref -); - -@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; - -compilations(unique int id: @compilation, string cwd: string ref); - -#keyset[id, num] -compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); - -#keyset[id, num, kind] -compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); - -diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); - -compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); - -#keyset[id, num] -compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); - -diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, - string full_error_message: string ref, int location: @location ref); - -locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, - int endLine: int ref, int endColumn: int ref); - -numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); - -files(unique int id: @file, string name: string ref); - -folders(unique int id: @folder, string name: string ref); - -containerparent(int parent: @container ref, unique int child: @container ref); - -has_location(unique int locatable: @locatable ref, int location: @location ref); - -#keyset[parent, idx] -comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); - -comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); - -doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); - -#keyset[parent, idx] -exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); - -literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); - -constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); - -fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); - -typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); - -#keyset[parent, idx] -stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); - -#keyset[parent, idx] -decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); - -#keyset[parent, idx] -specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); - -scopes(unique int id: @scope, int kind: int ref); - -scopenesting(unique int inner: @scope ref, int outer: @scope ref); - -scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); - -objects(unique int id: @object, int kind: int ref, string name: string ref); - -objectscopes(unique int object: @object ref, int scope: @scope ref); - -objecttypes(unique int object: @object ref, int tp: @type ref); - -methodreceivers(unique int method: @object ref, int receiver: @object ref); - -fieldstructs(unique int field: @object ref, int struct: @structtype ref); - -methodhosts(int method: @object ref, int host: @namedtype ref); - -defs(int ident: @ident ref, int object: @object ref); - -uses(int ident: @ident ref, int object: @object ref); - -types(unique int id: @type, int kind: int ref); - -type_of(unique int expr: @expr ref, int tp: @type ref); - -typename(unique int tp: @type ref, string name: string ref); - -key_type(unique int map: @maptype ref, int tp: @type ref); - -element_type(unique int container: @containertype ref, int tp: @type ref); - -base_type(unique int ptr: @pointertype ref, int tp: @type ref); - -underlying_type(unique int named: @namedtype ref, int tp: @type ref); - -#keyset[parent, index] -component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); - -array_length(unique int tp: @arraytype ref, string len: string ref); - -type_objects(unique int tp: @type ref, int object: @object ref); - -packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); - -#keyset[parent, idx] -modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); - -#keyset[parent, idx] -modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); - -#keyset[package, idx] -errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, - string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); - -has_ellipsis(int id: @callorconversionexpr ref); - -variadic(int id: @signaturetype ref); - -#keyset[parent, idx] -typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref, - int parent: @typeparamparentobject ref, int idx: int ref); - -@container = @file | @folder; - -@locatable = @xmllocatable | @node | @localscope; - -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent - | @scopenode | @comment_group | @comment; - -@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; - -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; - -@modexprparent = @file | @modexpr; - -@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; - -@stmtparent = @funcdef | @stmt | @decl; - -@declparent = @file | @declstmt; - -@typeparamdeclparent = @funcdecl | @typespec; - -@funcdef = @funclit | @funcdecl; - -@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; - -@location = @location_default; - -@sourceline = @locatable; - -case @comment.kind of - 0 = @slashslashcomment -| 1 = @slashstarcomment; - -case @expr.kind of - 0 = @badexpr -| 1 = @ident -| 2 = @ellipsis -| 3 = @intlit -| 4 = @floatlit -| 5 = @imaglit -| 6 = @charlit -| 7 = @stringlit -| 8 = @funclit -| 9 = @compositelit -| 10 = @parenexpr -| 11 = @selectorexpr -| 12 = @indexexpr -| 13 = @genericfunctioninstantiationexpr -| 14 = @generictypeinstantiationexpr -| 15 = @sliceexpr -| 16 = @typeassertexpr -| 17 = @callorconversionexpr -| 18 = @starexpr -| 19 = @keyvalueexpr -| 20 = @arraytypeexpr -| 21 = @structtypeexpr -| 22 = @functypeexpr -| 23 = @interfacetypeexpr -| 24 = @maptypeexpr -| 25 = @typesetliteralexpr -| 26 = @plusexpr -| 27 = @minusexpr -| 28 = @notexpr -| 29 = @complementexpr -| 30 = @derefexpr -| 31 = @addressexpr -| 32 = @arrowexpr -| 33 = @lorexpr -| 34 = @landexpr -| 35 = @eqlexpr -| 36 = @neqexpr -| 37 = @lssexpr -| 38 = @leqexpr -| 39 = @gtrexpr -| 40 = @geqexpr -| 41 = @addexpr -| 42 = @subexpr -| 43 = @orexpr -| 44 = @xorexpr -| 45 = @mulexpr -| 46 = @quoexpr -| 47 = @remexpr -| 48 = @shlexpr -| 49 = @shrexpr -| 50 = @andexpr -| 51 = @andnotexpr -| 52 = @sendchantypeexpr -| 53 = @recvchantypeexpr -| 54 = @sendrcvchantypeexpr -| 55 = @errorexpr; - -@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; - -@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; - -@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; - -@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; - -@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; - -@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; - -@logicalunaryexpr = @notexpr; - -@bitwiseunaryexpr = @complementexpr; - -@arithmeticunaryexpr = @plusexpr | @minusexpr; - -@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; - -@logicalbinaryexpr = @lorexpr | @landexpr; - -@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; - -@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; - -@shiftexpr = @shlexpr | @shrexpr; - -@comparison = @equalitytest | @relationalcomparison; - -@equalitytest = @eqlexpr | @neqexpr; - -@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; - -@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; - -case @stmt.kind of - 0 = @badstmt -| 1 = @declstmt -| 2 = @emptystmt -| 3 = @labeledstmt -| 4 = @exprstmt -| 5 = @sendstmt -| 6 = @incstmt -| 7 = @decstmt -| 8 = @gostmt -| 9 = @deferstmt -| 10 = @returnstmt -| 11 = @breakstmt -| 12 = @continuestmt -| 13 = @gotostmt -| 14 = @fallthroughstmt -| 15 = @blockstmt -| 16 = @ifstmt -| 17 = @caseclause -| 18 = @exprswitchstmt -| 19 = @typeswitchstmt -| 20 = @commclause -| 21 = @selectstmt -| 22 = @forstmt -| 23 = @rangestmt -| 24 = @assignstmt -| 25 = @definestmt -| 26 = @addassignstmt -| 27 = @subassignstmt -| 28 = @mulassignstmt -| 29 = @quoassignstmt -| 30 = @remassignstmt -| 31 = @andassignstmt -| 32 = @orassignstmt -| 33 = @xorassignstmt -| 34 = @shlassignstmt -| 35 = @shrassignstmt -| 36 = @andnotassignstmt; - -@incdecstmt = @incstmt | @decstmt; - -@assignment = @simpleassignstmt | @compoundassignstmt; - -@simpleassignstmt = @assignstmt | @definestmt; - -@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt - | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; - -@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; - -@switchstmt = @exprswitchstmt | @typeswitchstmt; - -@loopstmt = @forstmt | @rangestmt; - -case @decl.kind of - 0 = @baddecl -| 1 = @importdecl -| 2 = @constdecl -| 3 = @typedecl -| 4 = @vardecl -| 5 = @funcdecl; - -@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; - -case @spec.kind of - 0 = @importspec -| 1 = @valuespec -| 2 = @typedefspec -| 3 = @aliasspec; - -@typespec = @typedefspec | @aliasspec; - -case @object.kind of - 0 = @pkgobject -| 1 = @decltypeobject -| 2 = @builtintypeobject -| 3 = @declconstobject -| 4 = @builtinconstobject -| 5 = @declvarobject -| 6 = @declfunctionobject -| 7 = @builtinfunctionobject -| 8 = @labelobject; - -@typeparamparentobject = @decltypeobject | @declfunctionobject; - -@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; - -@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; - -@typeobject = @decltypeobject | @builtintypeobject; - -@valueobject = @constobject | @varobject | @functionobject; - -@constobject = @declconstobject | @builtinconstobject; - -@varobject = @declvarobject; - -@functionobject = @declfunctionobject | @builtinfunctionobject; - -case @scope.kind of - 0 = @universescope -| 1 = @packagescope -| 2 = @localscope; - -case @type.kind of - 0 = @invalidtype -| 1 = @boolexprtype -| 2 = @inttype -| 3 = @int8type -| 4 = @int16type -| 5 = @int32type -| 6 = @int64type -| 7 = @uinttype -| 8 = @uint8type -| 9 = @uint16type -| 10 = @uint32type -| 11 = @uint64type -| 12 = @uintptrtype -| 13 = @float32type -| 14 = @float64type -| 15 = @complex64type -| 16 = @complex128type -| 17 = @stringexprtype -| 18 = @unsafepointertype -| 19 = @boolliteraltype -| 20 = @intliteraltype -| 21 = @runeliteraltype -| 22 = @floatliteraltype -| 23 = @complexliteraltype -| 24 = @stringliteraltype -| 25 = @nilliteraltype -| 26 = @typeparamtype -| 27 = @arraytype -| 28 = @slicetype -| 29 = @structtype -| 30 = @pointertype -| 31 = @interfacetype -| 32 = @tupletype -| 33 = @signaturetype -| 34 = @maptype -| 35 = @sendchantype -| 36 = @recvchantype -| 37 = @sendrcvchantype -| 38 = @namedtype -| 39 = @typesetliteraltype; - -@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; - -@booltype = @boolexprtype | @boolliteraltype; - -@numerictype = @integertype | @floattype | @complextype; - -@integertype = @signedintegertype | @unsignedintegertype; - -@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; - -@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; - -@floattype = @float32type | @float64type | @floatliteraltype; - -@complextype = @complex64type | @complex128type | @complexliteraltype; - -@stringtype = @stringexprtype | @stringliteraltype; - -@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype - | @stringliteraltype | @nilliteraltype; - -@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype - | @signaturetype | @namedtype | @typesetliteraltype; - -@containertype = @arraytype | @slicetype | @maptype | @chantype; - -@chantype = @sendchantype | @recvchantype | @sendrcvchantype; - -case @modexpr.kind of - 0 = @modcommentblock -| 1 = @modline -| 2 = @modlineblock -| 3 = @modlparen -| 4 = @modrparen; - -case @error.kind of - 0 = @unknownerror -| 1 = @listerror -| 2 = @parseerror -| 3 = @typeerror; - diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql deleted file mode 100644 index 0b02cc680..000000000 --- a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql +++ /dev/null @@ -1,31 +0,0 @@ -class Type_ extends @type { - string toString() { result = "Type" } -} - -/** - * A new kind has been inserted such that `@arraytype` which used to have index - * 26 now has index 27. Another new kind has been inserted at 39, which is the - * end of the list. Entries with lower indices are unchanged. - */ -bindingset[new_index] -int old_index(int new_index) { - if new_index < 26 - then result = new_index - else - if new_index = 26 - then result = 0 // invalidtype - else - if new_index < 39 - then result + (27 - 26) = new_index - else result = 0 // invalidtype -} - -// The schema for types is: -// -// types(unique int id: @type, -// int kind: int ref); -from Type_ type, int new_kind, int old_kind -where - types(type, new_kind) and - old_kind = old_index(new_kind) -select type, old_kind diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties deleted file mode 100644 index a233cc281..000000000 --- a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties +++ /dev/null @@ -1,6 +0,0 @@ -description: Add generic instantiation expressions and type parameter types -compatibility: full -exprs.rel: run exprs.qlo -types.rel: run types.qlo -typeparamdecls.rel: delete -typeparam.rel: delete \ No newline at end of file diff --git a/downgrades/initial/go.dbscheme b/downgrades/initial/go.dbscheme deleted file mode 100644 index 8f168c8af..000000000 --- a/downgrades/initial/go.dbscheme +++ /dev/null @@ -1,531 +0,0 @@ -/** Auto-generated dbscheme; do not edit. */ - - -/** Duplicate code **/ - -duplicateCode( - unique int id : @duplication, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -similarCode( - unique int id : @similarity, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -@duplication_or_similarity = @duplication | @similarity; - -tokens( - int id : @duplication_or_similarity ref, - int offset : int ref, - int beginLine : int ref, - int beginColumn : int ref, - int endLine : int ref, - int endColumn : int ref); - -/** External data **/ - -externalData( - int id : @externalDataElement, - varchar(900) path : string ref, - int column: int ref, - varchar(900) value : string ref -); - -snapshotDate(unique date snapshotDate : date ref); - -sourceLocationPrefix(varchar(900) prefix : string ref); - - -/* - * XML Files - */ - -xmlEncoding( - unique int id: @file ref, - string encoding: string ref -); - -xmlDTDs( - unique int id: @xmldtd, - string root: string ref, - string publicId: string ref, - string systemId: string ref, - int fileid: @file ref -); - -xmlElements( - unique int id: @xmlelement, - string name: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int fileid: @file ref -); - -xmlAttrs( - unique int id: @xmlattribute, - int elementid: @xmlelement ref, - string name: string ref, - string value: string ref, - int idx: int ref, - int fileid: @file ref -); - -xmlNs( - int id: @xmlnamespace, - string prefixName: string ref, - string URI: string ref, - int fileid: @file ref -); - -xmlHasNs( - int elementId: @xmlnamespaceable ref, - int nsId: @xmlnamespace ref, - int fileid: @file ref -); - -xmlComments( - unique int id: @xmlcomment, - string text: string ref, - int parentid: @xmlparent ref, - int fileid: @file ref -); - -xmlChars( - unique int id: @xmlcharacters, - string text: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int isCDATA: int ref, - int fileid: @file ref -); - -@xmlparent = @file | @xmlelement; -@xmlnamespaceable = @xmlelement | @xmlattribute; - -xmllocations( - int xmlElement: @xmllocatable ref, - int location: @location_default ref -); - -@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; - -compilations(unique int id: @compilation, string cwd: string ref); - -#keyset[id, num] -compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); - -#keyset[id, num, kind] -compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); - -diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); - -compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); - -#keyset[id, num] -compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); - -diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, - string full_error_message: string ref, int location: @location ref); - -locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, - int endLine: int ref, int endColumn: int ref); - -numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); - -files(unique int id: @file, string name: string ref); - -folders(unique int id: @folder, string name: string ref); - -containerparent(int parent: @container ref, unique int child: @container ref); - -has_location(unique int locatable: @locatable ref, int location: @location ref); - -#keyset[parent, idx] -comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); - -comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); - -doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); - -#keyset[parent, idx] -exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); - -literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); - -constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); - -fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); - -#keyset[parent, idx] -stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); - -#keyset[parent, idx] -decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); - -#keyset[parent, idx] -specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); - -scopes(unique int id: @scope, int kind: int ref); - -scopenesting(unique int inner: @scope ref, int outer: @scope ref); - -scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); - -objects(unique int id: @object, int kind: int ref, string name: string ref); - -objectscopes(unique int object: @object ref, int scope: @scope ref); - -objecttypes(unique int object: @object ref, int tp: @type ref); - -methodreceivers(unique int method: @object ref, int receiver: @object ref); - -fieldstructs(unique int field: @object ref, int struct: @structtype ref); - -methodhosts(int method: @object ref, int host: @namedtype ref); - -defs(int ident: @ident ref, int object: @object ref); - -uses(int ident: @ident ref, int object: @object ref); - -types(unique int id: @type, int kind: int ref); - -type_of(unique int expr: @expr ref, int tp: @type ref); - -typename(unique int tp: @type ref, string name: string ref); - -key_type(unique int map: @maptype ref, int tp: @type ref); - -element_type(unique int container: @containertype ref, int tp: @type ref); - -base_type(unique int ptr: @pointertype ref, int tp: @type ref); - -underlying_type(unique int named: @namedtype ref, int tp: @type ref); - -#keyset[parent, index] -component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); - -array_length(unique int tp: @arraytype ref, string len: string ref); - -type_objects(unique int tp: @type ref, int object: @object ref); - -packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); - -#keyset[parent, idx] -modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); - -#keyset[parent, idx] -modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); - -#keyset[package, idx] -errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, - string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); - -has_ellipsis(int id: @callorconversionexpr ref); - -variadic(int id: @signaturetype ref); - -@container = @file | @folder; - -@locatable = @xmllocatable | @node | @localscope; - -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode - | @comment_group | @comment; - -@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; - -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; - -@modexprparent = @file | @modexpr; - -@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; - -@stmtparent = @funcdef | @stmt | @decl; - -@declparent = @file | @declstmt; - -@funcdef = @funclit | @funcdecl; - -@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; - -@location = @location_default; - -@sourceline = @locatable; - -case @comment.kind of - 0 = @slashslashcomment -| 1 = @slashstarcomment; - -case @expr.kind of - 0 = @badexpr -| 1 = @ident -| 2 = @ellipsis -| 3 = @intlit -| 4 = @floatlit -| 5 = @imaglit -| 6 = @charlit -| 7 = @stringlit -| 8 = @funclit -| 9 = @compositelit -| 10 = @parenexpr -| 11 = @selectorexpr -| 12 = @indexexpr -| 13 = @sliceexpr -| 14 = @typeassertexpr -| 15 = @callorconversionexpr -| 16 = @starexpr -| 17 = @keyvalueexpr -| 18 = @arraytypeexpr -| 19 = @structtypeexpr -| 20 = @functypeexpr -| 21 = @interfacetypeexpr -| 22 = @maptypeexpr -| 23 = @plusexpr -| 24 = @minusexpr -| 25 = @notexpr -| 26 = @complementexpr -| 27 = @derefexpr -| 28 = @addressexpr -| 29 = @arrowexpr -| 30 = @lorexpr -| 31 = @landexpr -| 32 = @eqlexpr -| 33 = @neqexpr -| 34 = @lssexpr -| 35 = @leqexpr -| 36 = @gtrexpr -| 37 = @geqexpr -| 38 = @addexpr -| 39 = @subexpr -| 40 = @orexpr -| 41 = @xorexpr -| 42 = @mulexpr -| 43 = @quoexpr -| 44 = @remexpr -| 45 = @shlexpr -| 46 = @shrexpr -| 47 = @andexpr -| 48 = @andnotexpr -| 49 = @sendchantypeexpr -| 50 = @recvchantypeexpr -| 51 = @sendrcvchantypeexpr -| 52 = @errorexpr; - -@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; - -@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; - -@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; - -@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; - -@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; - -@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; - -@logicalunaryexpr = @notexpr; - -@bitwiseunaryexpr = @complementexpr; - -@arithmeticunaryexpr = @plusexpr | @minusexpr; - -@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; - -@logicalbinaryexpr = @lorexpr | @landexpr; - -@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; - -@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; - -@shiftexpr = @shlexpr | @shrexpr; - -@comparison = @equalitytest | @relationalcomparison; - -@equalitytest = @eqlexpr | @neqexpr; - -@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; - -@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; - -case @stmt.kind of - 0 = @badstmt -| 1 = @declstmt -| 2 = @emptystmt -| 3 = @labeledstmt -| 4 = @exprstmt -| 5 = @sendstmt -| 6 = @incstmt -| 7 = @decstmt -| 8 = @gostmt -| 9 = @deferstmt -| 10 = @returnstmt -| 11 = @breakstmt -| 12 = @continuestmt -| 13 = @gotostmt -| 14 = @fallthroughstmt -| 15 = @blockstmt -| 16 = @ifstmt -| 17 = @caseclause -| 18 = @exprswitchstmt -| 19 = @typeswitchstmt -| 20 = @commclause -| 21 = @selectstmt -| 22 = @forstmt -| 23 = @rangestmt -| 24 = @assignstmt -| 25 = @definestmt -| 26 = @addassignstmt -| 27 = @subassignstmt -| 28 = @mulassignstmt -| 29 = @quoassignstmt -| 30 = @remassignstmt -| 31 = @andassignstmt -| 32 = @orassignstmt -| 33 = @xorassignstmt -| 34 = @shlassignstmt -| 35 = @shrassignstmt -| 36 = @andnotassignstmt; - -@incdecstmt = @incstmt | @decstmt; - -@assignment = @simpleassignstmt | @compoundassignstmt; - -@simpleassignstmt = @assignstmt | @definestmt; - -@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt - | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; - -@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; - -@switchstmt = @exprswitchstmt | @typeswitchstmt; - -@loopstmt = @forstmt | @rangestmt; - -case @decl.kind of - 0 = @baddecl -| 1 = @importdecl -| 2 = @constdecl -| 3 = @typedecl -| 4 = @vardecl -| 5 = @funcdecl; - -@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; - -case @spec.kind of - 0 = @importspec -| 1 = @valuespec -| 2 = @typedefspec -| 3 = @aliasspec; - -@typespec = @typedefspec | @aliasspec; - -case @object.kind of - 0 = @pkgobject -| 1 = @decltypeobject -| 2 = @builtintypeobject -| 3 = @declconstobject -| 4 = @builtinconstobject -| 5 = @declvarobject -| 6 = @declfunctionobject -| 7 = @builtinfunctionobject -| 8 = @labelobject; - -@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; - -@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; - -@typeobject = @decltypeobject | @builtintypeobject; - -@valueobject = @constobject | @varobject | @functionobject; - -@constobject = @declconstobject | @builtinconstobject; - -@varobject = @declvarobject; - -@functionobject = @declfunctionobject | @builtinfunctionobject; - -case @scope.kind of - 0 = @universescope -| 1 = @packagescope -| 2 = @localscope; - -case @type.kind of - 0 = @invalidtype -| 1 = @boolexprtype -| 2 = @inttype -| 3 = @int8type -| 4 = @int16type -| 5 = @int32type -| 6 = @int64type -| 7 = @uinttype -| 8 = @uint8type -| 9 = @uint16type -| 10 = @uint32type -| 11 = @uint64type -| 12 = @uintptrtype -| 13 = @float32type -| 14 = @float64type -| 15 = @complex64type -| 16 = @complex128type -| 17 = @stringexprtype -| 18 = @unsafepointertype -| 19 = @boolliteraltype -| 20 = @intliteraltype -| 21 = @runeliteraltype -| 22 = @floatliteraltype -| 23 = @complexliteraltype -| 24 = @stringliteraltype -| 25 = @nilliteraltype -| 26 = @arraytype -| 27 = @slicetype -| 28 = @structtype -| 29 = @pointertype -| 30 = @interfacetype -| 31 = @tupletype -| 32 = @signaturetype -| 33 = @maptype -| 34 = @sendchantype -| 35 = @recvchantype -| 36 = @sendrcvchantype -| 37 = @namedtype; - -@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; - -@booltype = @boolexprtype | @boolliteraltype; - -@numerictype = @integertype | @floattype | @complextype; - -@integertype = @signedintegertype | @unsignedintegertype; - -@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; - -@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; - -@floattype = @float32type | @float64type | @floatliteraltype; - -@complextype = @complex64type | @complex128type | @complexliteraltype; - -@stringtype = @stringexprtype | @stringliteraltype; - -@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype - | @stringliteraltype | @nilliteraltype; - -@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; - -@containertype = @arraytype | @slicetype | @maptype | @chantype; - -@chantype = @sendchantype | @recvchantype | @sendrcvchantype; - -case @modexpr.kind of - 0 = @modcommentblock -| 1 = @modline -| 2 = @modlineblock -| 3 = @modlparen -| 4 = @modrparen; - -case @error.kind of - 0 = @unknownerror -| 1 = @listerror -| 2 = @parseerror -| 3 = @typeerror; - diff --git a/downgrades/qlpack.yml b/downgrades/qlpack.yml deleted file mode 100644 index d3e056bea..000000000 --- a/downgrades/qlpack.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: codeql/go-downgrades -groups: go -downgrades: . -library: true diff --git a/external-packs/codeql/suite-helpers/0.0.2/code-scanning-selectors.yml b/external-packs/codeql/suite-helpers/0.0.2/code-scanning-selectors.yml deleted file mode 100644 index 116d7288d..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/code-scanning-selectors.yml +++ /dev/null @@ -1,27 +0,0 @@ -- description: Selectors for selecting the Code-Scanning-relevant queries for a language -- include: - kind: - - problem - - path-problem - - alert - - path-alert - precision: - - high - - very-high - problem.severity: - - error - - warning - tags contain: - - security -- include: - kind: - - diagnostic -- include: - kind: - - metric - tags contain: - - summary -- exclude: - deprecated: // -- exclude: - query path: /^experimental\/.*/ diff --git a/external-packs/codeql/suite-helpers/0.0.2/lgtm-displayed-only.yml b/external-packs/codeql/suite-helpers/0.0.2/lgtm-displayed-only.yml deleted file mode 100644 index 1b7237495..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/lgtm-displayed-only.yml +++ /dev/null @@ -1,12 +0,0 @@ -- description: Selectors for excluding queries that LGTM doesn't display by default -- exclude: - kind: - - problem - - path-problem - precision: medium -- exclude: - kind: - - problem - - path-problem - precision: high - problem.severity: recommendation diff --git a/external-packs/codeql/suite-helpers/0.0.2/lgtm-selectors.yml b/external-packs/codeql/suite-helpers/0.0.2/lgtm-selectors.yml deleted file mode 100644 index c83484cb1..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/lgtm-selectors.yml +++ /dev/null @@ -1,25 +0,0 @@ -- description: Selectors for selecting the LGTM-relevant queries for a language -- include: - kind: - - problem - - path-problem - precision: - - high - - very-high -- include: - kind: - - problem - - path-problem - precision: medium - problem.severity: - - error - - warning -- include: - kind: - - definitions - - alert-suppression - - file-classifier -- exclude: - deprecated: // -- exclude: - query path: /^experimental\/.*/ diff --git a/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml b/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml deleted file mode 100644 index ca0a6732f..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml +++ /dev/null @@ -1,3 +0,0 @@ -name: codeql/suite-helpers -version: 0.0.2 -library: true diff --git a/external-packs/codeql/suite-helpers/0.0.2/security-and-quality-selectors.yml b/external-packs/codeql/suite-helpers/0.0.2/security-and-quality-selectors.yml deleted file mode 100644 index 61466f538..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/security-and-quality-selectors.yml +++ /dev/null @@ -1,29 +0,0 @@ -- description: Selectors for selecting the security-and-quality queries for a language -- include: - kind: - - problem - - path-problem - precision: - - high - - very-high -- include: - kind: - - problem - - path-problem - precision: medium - problem.severity: - - error - - warning -- include: - kind: - - diagnostic -- include: - kind: - - metric - tags contain: - - summary -- exclude: - deprecated: // -- exclude: - query path: /^experimental\/.*/ - diff --git a/external-packs/codeql/suite-helpers/0.0.2/security-extended-selectors.yml b/external-packs/codeql/suite-helpers/0.0.2/security-extended-selectors.yml deleted file mode 100644 index c3a82de14..000000000 --- a/external-packs/codeql/suite-helpers/0.0.2/security-extended-selectors.yml +++ /dev/null @@ -1,34 +0,0 @@ -- description: Selectors for selecting the security-extended queries for a language -- include: - kind: - - problem - - path-problem - precision: - - high - - very-high - tags contain: - - security -- include: - kind: - - problem - - path-problem - precision: - - medium - problem.severity: - - error - - warning - tags contain: - - security -- include: - kind: - - diagnostic -- include: - kind: - - metric - tags contain: - - summary -- exclude: - deprecated: // -- exclude: - query path: /^experimental\/.*/ - diff --git a/extractor-smoke-test/.gitignore b/extractor-smoke-test/.gitignore deleted file mode 100644 index e32b11d7e..000000000 --- a/extractor-smoke-test/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.bqrs -tracing-out.csv -notracing-out.csv -testdb diff --git a/extractor-smoke-test/expected.csv b/extractor-smoke-test/expected.csv deleted file mode 100644 index cb0eb08f8..000000000 --- a/extractor-smoke-test/expected.csv +++ /dev/null @@ -1,12 +0,0 @@ -"1","assignment to i" -"assignment to i","selection of Println" -"call to Println","exit" -"entry","skip" -"entry","skip" -"function declaration","exit" -"i","call to Println" -"nd","col1" -"selection of Println","i" -"skip","1" -"skip","function declaration" -"skip","skip" diff --git a/extractor-smoke-test/go.mod b/extractor-smoke-test/go.mod deleted file mode 100644 index 0659b4eae..000000000 --- a/extractor-smoke-test/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/codeql-go-extractor-smoke-test - -go 1.14 diff --git a/extractor-smoke-test/main.go b/extractor-smoke-test/main.go deleted file mode 100644 index 2f7b1325d..000000000 --- a/extractor-smoke-test/main.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import "fmt" - -func main() { - var i int = 1 - fmt.Println(i) -} diff --git a/extractor-smoke-test/test.sh b/extractor-smoke-test/test.sh deleted file mode 100755 index 7279febb9..000000000 --- a/extractor-smoke-test/test.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -set -e - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -cd $DIR - -rm -rf testdb - -codeql database create --language=go testdb --search-path .. -codeql dataset check testdb/db-go -codeql query run ../ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.ql --database=testdb --output=notracing-out.bqrs --search-path .. -codeql bqrs decode notracing-out.bqrs --format=csv --output=notracing-out.csv -diff -w -u <(sort notracing-out.csv) expected.csv - -# Now do it again with tracing enabled - -export CODEQL_EXTRACTOR_GO_BUILD_TRACING=on - -rm -rf testdb - -codeql database create --language=go testdb --search-path .. -codeql dataset check testdb/db-go -codeql query run ../ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.ql --database=testdb --output=tracing-out.bqrs --search-path .. -codeql bqrs decode tracing-out.bqrs --format=csv --output=tracing-out.csv -diff -w -u <(sort tracing-out.csv) expected.csv diff --git a/extractor/autobuilder/autobuilder.go b/extractor/autobuilder/autobuilder.go deleted file mode 100644 index 11c49735e..000000000 --- a/extractor/autobuilder/autobuilder.go +++ /dev/null @@ -1,81 +0,0 @@ -// Package autobuilder implements a simple system that attempts to run build commands for common -// build frameworks, if the relevant files exist. -package autobuilder - -import ( - "log" - "os" - "os/exec" - - "github.com/github/codeql-go/extractor/util" -) - -// CheckExtracted sets whether the autobuilder should check whether source files have been extracted -// to the CodeQL source directory as well as whether the build command executed successfully. -var CheckExtracted = false - -// checkEmpty checks whether a directory either doesn't exist or is empty. -func checkEmpty(dir string) (bool, error) { - if !util.DirExists(dir) { - return true, nil - } - - d, err := os.Open(dir) - if err != nil { - return false, err - } - defer d.Close() - - names, err := d.Readdirnames(-1) - if err != nil { - return false, err - } - return len(names) == 0, nil -} - -// checkExtractorRun checks whether the CodeQL Go extractor has run, by checking if the source -// archive directory is empty or not. -func checkExtractorRun() bool { - srcDir := os.Getenv("CODEQL_EXTRACTOR_GO_SOURCE_ARCHIVE_DIR") - if srcDir != "" { - empty, err := checkEmpty(srcDir) - if err != nil { - log.Fatalf("Unable to read source archive directory %s.", srcDir) - } - if empty { - log.Printf("No Go code seen; continuing to try other builds.") - return false - } - return true - } else { - log.Fatalf("No source directory set.\nThis binary should not be run manually; instead, use the CodeQL CLI or VSCode extension. See https://securitylab.github.com/tools/codeql.") - return false - } -} - -// tryBuildIfExists tries to run the command `cmd args...` if the file `buildFile` exists and is not -// a directory. Returns true if the command was successful and false if not. -func tryBuildIfExists(buildFile, cmd string, args ...string) bool { - if util.FileExists(buildFile) { - log.Printf("%s found.\n", buildFile) - return tryBuild(cmd, args...) - } - return false -} - -// tryBuild tries to run `cmd args...`, returning true if successful and false if not. -func tryBuild(cmd string, args ...string) bool { - log.Printf("Trying build command %s %v", cmd, args) - res := util.RunCmd(exec.Command(cmd, args...)) - return res && (!CheckExtracted || checkExtractorRun()) -} - -// Autobuild attempts to detect build system and run the corresponding command. -func Autobuild() bool { - return tryBuildIfExists("Makefile", "make") || - tryBuildIfExists("makefile", "make") || - tryBuildIfExists("GNUmakefile", "make") || - tryBuildIfExists("build.ninja", "ninja") || - tryBuildIfExists("build", "./build") || - tryBuildIfExists("build.sh", "./build.sh") -} diff --git a/extractor/cli/go-autobuilder/go-autobuilder.go b/extractor/cli/go-autobuilder/go-autobuilder.go deleted file mode 100644 index 07b8a6cb4..000000000 --- a/extractor/cli/go-autobuilder/go-autobuilder.go +++ /dev/null @@ -1,588 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "net/url" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strings" - - "golang.org/x/mod/semver" - - "github.com/github/codeql-go/extractor/autobuilder" - "github.com/github/codeql-go/extractor/util" -) - -func usage() { - fmt.Fprintf(os.Stderr, - `%s is a wrapper script that installs dependencies and calls the extractor. - -When LGTM_SRC is not set, the script installs dependencies as described below, and then invokes the -extractor in the working directory. - -If LGTM_SRC is set, it checks for the presence of the files 'go.mod', 'Gopkg.toml', and -'glide.yaml' to determine how to install dependencies: if a 'Gopkg.toml' file is present, it uses -'dep ensure', if there is a 'glide.yaml' it uses 'glide install', and otherwise 'go get'. -Additionally, unless a 'go.mod' file is detected, it sets up a temporary GOPATH and moves all -source files into a folder corresponding to the package's import path before installing -dependencies. - -This behavior can be further customized using environment variables: setting LGTM_INDEX_NEED_GOPATH -to 'false' disables the GOPATH set-up, CODEQL_EXTRACTOR_GO_BUILD_COMMAND (or alternatively -LGTM_INDEX_BUILD_COMMAND), can be set to a newline-separated list of commands to run in order to -install dependencies, and LGTM_INDEX_IMPORT_PATH can be used to override the package import path, -which is otherwise inferred from the SEMMLE_REPO_URL or GITHUB_REPOSITORY environment variables. - -In resource-constrained environments, the environment variable CODEQL_EXTRACTOR_GO_MAX_GOROUTINES -(or its legacy alias SEMMLE_MAX_GOROUTINES) can be used to limit the number of parallel goroutines -started by the extractor, which reduces CPU and memory requirements. The default value for this -variable is 32. -`, - os.Args[0]) - fmt.Fprintf(os.Stderr, "Usage:\n\n %s\n", os.Args[0]) -} - -var goVersion = "" - -// Returns the current Go version as returned by 'go version', e.g. go1.14.4 -func getEnvGoVersion() string { - if goVersion == "" { - gover, err := exec.Command("go", "version").CombinedOutput() - if err != nil { - log.Fatalf("Unable to run the go command, is it installed?\nError: %s", err.Error()) - } - goVersion = strings.Fields(string(gover))[2] - } - return goVersion -} - -// Returns the current Go version in semver format, e.g. v1.14.4 -func getEnvGoSemVer() string { - goVersion := getEnvGoVersion() - if !strings.HasPrefix(goVersion, "go") { - log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion) - } - return "v" + goVersion[2:] -} - -func tryBuild(buildFile, cmd string, args ...string) bool { - if util.FileExists(buildFile) { - log.Printf("%s found, running %s\n", buildFile, cmd) - return util.RunCmd(exec.Command(cmd, args...)) - } - return false -} - -func getImportPath() (importpath string) { - importpath = os.Getenv("LGTM_INDEX_IMPORT_PATH") - if importpath == "" { - repourl := os.Getenv("SEMMLE_REPO_URL") - if repourl == "" { - githubrepo := os.Getenv("GITHUB_REPOSITORY") - if githubrepo == "" { - log.Printf("Unable to determine import path, as neither LGTM_INDEX_IMPORT_PATH nor GITHUB_REPOSITORY is set\n") - return "" - } else { - importpath = "github.com/" + githubrepo - } - } else { - importpath = getImportPathFromRepoURL(repourl) - if importpath == "" { - log.Printf("Failed to determine import path from SEMMLE_REPO_URL '%s'\n", repourl) - return - } - } - } - log.Printf("Import path is '%s'\n", importpath) - return -} - -func getImportPathFromRepoURL(repourl string) string { - // check for scp-like URL as in "git@github.com:github/codeql-go.git" - shorturl := regexp.MustCompile("^([^@]+@)?([^:]+):([^/].*?)(\\.git)?$") - m := shorturl.FindStringSubmatch(repourl) - if m != nil { - return m[2] + "/" + m[3] - } - - // otherwise parse as proper URL - u, err := url.Parse(repourl) - if err != nil { - log.Fatalf("Malformed repository URL '%s'\n", repourl) - } - - if u.Scheme == "file" { - // we can't determine import paths from file paths - return "" - } - - if u.Hostname() == "" || u.Path == "" { - return "" - } - - host := u.Hostname() - path := u.Path - // strip off leading slashes and trailing `.git` if present - path = regexp.MustCompile("^/+|\\.git$").ReplaceAllString(path, "") - return host + "/" + path -} - -func restoreRepoLayout(fromDir string, dirEntries []string, scratchDirName string, toDir string) { - for _, dirEntry := range dirEntries { - if dirEntry != scratchDirName { - log.Printf("Restoring %s/%s to %s/%s.\n", fromDir, dirEntry, toDir, dirEntry) - err := os.Rename(filepath.Join(fromDir, dirEntry), filepath.Join(toDir, dirEntry)) - if err != nil { - log.Printf("Failed to move file/directory %s from directory %s to directory %s: %s\n", dirEntry, fromDir, toDir, err.Error()) - } - } - } -} - -// DependencyInstallerMode is an enum describing how dependencies should be installed -type DependencyInstallerMode int - -const ( - // GoGetNoModules represents dependency installation using `go get` without modules - GoGetNoModules DependencyInstallerMode = iota - // GoGetWithModules represents dependency installation using `go get` with modules - GoGetWithModules - // Dep represent dependency installation using `dep ensure` - Dep - // Glide represents dependency installation using `glide install` - Glide -) - -// ModMode corresponds to the possible values of the -mod flag for the Go compiler -type ModMode int - -const ( - ModUnset ModMode = iota - ModReadonly - ModMod - ModVendor -) - -func (m ModMode) argsForGoVersion(version string) []string { - switch m { - case ModUnset: - return []string{} - case ModReadonly: - return []string{"-mod=readonly"} - case ModMod: - if !semver.IsValid(version) { - log.Fatalf("Invalid Go semver: '%s'", version) - } - if semver.Compare(version, "v1.14") < 0 { - return []string{} // -mod=mod is the default behaviour for go <= 1.13, and is not accepted as an argument - } else { - return []string{"-mod=mod"} - } - case ModVendor: - return []string{"-mod=vendor"} - } - return nil -} - -// addVersionToMod add a go version directive, e.g. `go 1.14` to a `go.mod` file. -func addVersionToMod(goMod []byte, version string) bool { - cmd := exec.Command("go", "mod", "edit", "-go="+version) - return util.RunCmd(cmd) -} - -// checkVendor tests to see whether a vendor directory is inconsistent according to the go frontend -func checkVendor() bool { - vendorCheckCmd := exec.Command("go", "list", "-mod=vendor", "./...") - outp, err := vendorCheckCmd.CombinedOutput() - if err != nil { - badVendorRe := regexp.MustCompile(`(?m)^go: inconsistent vendoring in .*:$`) - return !badVendorRe.Match(outp) - } - - return true -} - -func main() { - if len(os.Args) > 1 { - usage() - os.Exit(2) - } - - log.Printf("Autobuilder was built with %s, environment has %s\n", runtime.Version(), getEnvGoVersion()) - - srcdir := os.Getenv("LGTM_SRC") - inLGTM := srcdir != "" - if inLGTM { - log.Printf("LGTM_SRC is %s\n", srcdir) - } else { - cwd, err := os.Getwd() - if err != nil { - log.Fatalln("Failed to get current working directory.") - } - log.Printf("LGTM_SRC is not set; defaulting to current working directory %s\n", cwd) - srcdir = cwd - } - - // we set `SEMMLE_PATH_TRANSFORMER` ourselves in some cases, so blank it out first for consistency - os.Setenv("SEMMLE_PATH_TRANSFORMER", "") - - // determine how to install dependencies and whether a GOPATH needs to be set up before - // extraction - depMode := GoGetNoModules - modMode := ModUnset - needGopath := true - if _, present := os.LookupEnv("GO111MODULE"); !present { - os.Setenv("GO111MODULE", "auto") - } - if util.FileExists("go.mod") { - depMode = GoGetWithModules - needGopath = false - log.Println("Found go.mod, enabling go modules") - } else if util.FileExists("Gopkg.toml") { - depMode = Dep - log.Println("Found Gopkg.toml, using dep instead of go get") - } else if util.FileExists("glide.yaml") { - depMode = Glide - log.Println("Found glide.yaml, enabling go modules") - } - - if depMode == GoGetWithModules { - // if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and - // skip the dependency installation step and run the extractor with `-mod=vendor` - if util.FileExists("vendor/modules.txt") { - modMode = ModVendor - } else if util.DirExists("vendor") { - modMode = ModMod - } - } - - if modMode == ModVendor { - // fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod - // if this is the case, and dependencies were vendored with an old go version (and therefore - // do not contain a '## explicit' annotation, the go command will fail and refuse to do any - // work - // - // we work around this by adding an explicit go version of 1.13, which is the last version - // where this is not an issue - if depMode == GoGetWithModules { - goMod, err := ioutil.ReadFile("go.mod") - if err != nil { - log.Println("Failed to read go.mod to check for missing Go version") - } else if versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+[0-9]+\.[0-9]+$`); !versionRe.Match(goMod) { - // if the go.mod does not contain a version line - modulesTxt, err := ioutil.ReadFile("vendor/modules.txt") - if err != nil { - log.Println("Failed to read vendor/modules.txt to check for mismatched Go version") - } else if explicitRe := regexp.MustCompile("(?m)^## explicit$"); !explicitRe.Match(modulesTxt) { - // and the modules.txt does not contain an explicit annotation - log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations") - if !addVersionToMod(goMod, "1.13") { - log.Println("Failed to add a version to the go.mod file to fix explicitly required package bug; not using vendored dependencies") - modMode = ModMod - } - } - } - } - } - - // Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here: - if depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "1.16") >= 0 { - // stat go.mod and go.sum - beforeGoModFileInfo, beforeGoModErr := os.Stat("go.mod") - if beforeGoModErr != nil { - log.Println("Failed to stat go.mod before running `go mod tidy -e`") - } - - beforeGoSumFileInfo, beforeGoSumErr := os.Stat("go.sum") - - // run `go mod tidy -e` - res := util.RunCmd(exec.Command("go", "mod", "tidy", "-e")) - - if !res { - log.Println("Failed to run `go mod tidy -e`") - } else { - if beforeGoModFileInfo != nil { - afterGoModFileInfo, afterGoModErr := os.Stat("go.mod") - if afterGoModErr != nil { - log.Println("Failed to stat go.mod after running `go mod tidy -e`") - } else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) { - // if go.mod has been changed then notify the user - log.Println("We have run `go mod tidy -e` and it altered go.mod. You may wish to check these changes into version control. ") - } - } - - afterGoSumFileInfo, afterGoSumErr := os.Stat("go.sum") - if afterGoSumErr != nil { - log.Println("Failed to stat go.sum after running `go mod tidy -e`") - } else { - if beforeGoSumErr != nil || afterGoSumFileInfo.ModTime().After(beforeGoSumFileInfo.ModTime()) { - // if go.sum has been changed then notify the user - log.Println("We have run `go mod tidy -e` and it altered go.sum. You may wish to check these changes into version control. ") - } - } - } - } - - // if `LGTM_INDEX_NEED_GOPATH` is set, it overrides the value for `needGopath` inferred above - if needGopathOverride := os.Getenv("LGTM_INDEX_NEED_GOPATH"); needGopathOverride != "" { - inLGTM = true - if needGopathOverride == "true" { - needGopath = true - } else if needGopathOverride == "false" { - needGopath = false - } else { - log.Fatalf("Unexpected value for Boolean environment variable LGTM_NEED_GOPATH: %v.\n", needGopathOverride) - } - } - - importpath := getImportPath() - if needGopath && importpath == "" { - log.Printf("Failed to determine import path, not setting up GOPATH") - needGopath = false - } - - if inLGTM && needGopath { - // a temporary directory where everything is moved while the correct - // directory structure is created. - scratch, err := ioutil.TempDir(srcdir, "scratch") - if err != nil { - log.Fatalf("Failed to create temporary directory %s in directory %s: %s\n", - scratch, srcdir, err.Error()) - } - log.Printf("Temporary directory is %s.\n", scratch) - - // move all files in `srcdir` to `scratch` - dir, err := os.Open(srcdir) - if err != nil { - log.Fatalf("Failed to open source directory %s for reading: %s\n", srcdir, err.Error()) - } - files, err := dir.Readdirnames(-1) - if err != nil { - log.Fatalf("Failed to read source directory %s: %s\n", srcdir, err.Error()) - } - for _, file := range files { - if file != filepath.Base(scratch) { - log.Printf("Moving %s/%s to %s/%s.\n", srcdir, file, scratch, file) - err := os.Rename(filepath.Join(srcdir, file), filepath.Join(scratch, file)) - if err != nil { - log.Fatalf("Failed to move file %s to the temporary directory: %s\n", file, err.Error()) - } - } - } - - // create a new folder which we will add to GOPATH below - // Note we evaluate all symlinks here for consistency: otherwise os.Chdir below - // will follow links but other references to the path may not, which can lead to - // disagreements between GOPATH and the working directory. - realSrc, err := filepath.EvalSymlinks(srcdir) - if err != nil { - log.Fatalf("Failed to evaluate symlinks in %s: %s\n", srcdir, err.Error()) - } - - root := filepath.Join(realSrc, "root") - - // move source files to where Go expects them to be - newdir := filepath.Join(root, "src", importpath) - err = os.MkdirAll(filepath.Dir(newdir), 0755) - if err != nil { - log.Fatalf("Failed to create directory %s: %s\n", newdir, err.Error()) - } - log.Printf("Moving %s to %s.\n", scratch, newdir) - err = os.Rename(scratch, newdir) - if err != nil { - log.Fatalf("Failed to rename %s to %s: %s\n", scratch, newdir, err.Error()) - } - - // schedule restoring the contents of newdir to their original location after this function completes: - defer restoreRepoLayout(newdir, files, filepath.Base(scratch), srcdir) - - err = os.Chdir(newdir) - if err != nil { - log.Fatalf("Failed to chdir into %s: %s\n", newdir, err.Error()) - } - - // set up SEMMLE_PATH_TRANSFORMER to ensure paths in the source archive and the snapshot - // match the original source location, not the location we moved it to - pt, err := ioutil.TempFile("", "path-transformer") - if err != nil { - log.Fatalf("Unable to create path transformer file: %s.", err.Error()) - } - defer os.Remove(pt.Name()) - _, err = pt.WriteString("#" + realSrc + "\n" + newdir + "//\n") - if err != nil { - log.Fatalf("Unable to write path transformer file: %s.", err.Error()) - } - err = pt.Close() - if err != nil { - log.Fatalf("Unable to close path transformer file: %s.", err.Error()) - } - err = os.Setenv("SEMMLE_PATH_TRANSFORMER", pt.Name()) - if err != nil { - log.Fatalf("Unable to set SEMMLE_PATH_TRANSFORMER environment variable: %s.\n", err.Error()) - } - - // set/extend GOPATH - oldGopath := os.Getenv("GOPATH") - var newGopath string - if oldGopath != "" { - newGopath = strings.Join( - []string{root, oldGopath}, - string(os.PathListSeparator), - ) - } else { - newGopath = root - } - err = os.Setenv("GOPATH", newGopath) - if err != nil { - log.Fatalf("Unable to set GOPATH to %s: %s\n", newGopath, err.Error()) - } - log.Printf("GOPATH set to %s.\n", newGopath) - } - - // check whether an explicit dependency installation command was provided - inst := util.Getenv("CODEQL_EXTRACTOR_GO_BUILD_COMMAND", "LGTM_INDEX_BUILD_COMMAND") - shouldInstallDependencies := false - if inst == "" { - // try to build the project - buildSucceeded := autobuilder.Autobuild() - - // Build failed or there are still dependency errors; we'll try to install dependencies - // ourselves - if !buildSucceeded { - log.Println("Build failed, continuing to install dependencies.") - - shouldInstallDependencies = true - } else if util.DepErrors("./...", modMode.argsForGoVersion(getEnvGoSemVer())...) { - log.Println("Dependencies are still not resolving after the build, continuing to install dependencies.") - - shouldInstallDependencies = true - } - } else { - // write custom build commands into a script, then run it - var ( - ext = "" - header = "" - footer = "" - ) - if runtime.GOOS == "windows" { - ext = ".cmd" - header = "@echo on\n@prompt +$S\n" - footer = "\nIF %ERRORLEVEL% NEQ 0 EXIT" - } else { - ext = ".sh" - header = "#! /bin/bash\nset -xe +u\n" - } - script, err := ioutil.TempFile("", "go-build-command-*"+ext) - if err != nil { - log.Fatalf("Unable to create temporary script holding custom build commands: %s\n", err.Error()) - } - defer os.Remove(script.Name()) - _, err = script.WriteString(header + inst + footer) - if err != nil { - log.Fatalf("Unable to write to temporary script holding custom build commands: %s\n", err.Error()) - } - err = script.Close() - if err != nil { - log.Fatalf("Unable to close temporary script holding custom build commands: %s\n", err.Error()) - } - os.Chmod(script.Name(), 0700) - log.Println("Installing dependencies using custom build command.") - util.RunCmd(exec.Command(script.Name())) - } - - if modMode == ModVendor { - // test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod - // or not set if the go version < 1.14. Note we check this post-build in case the build brings - // the vendor directory up to date. - if !checkVendor() { - modMode = ModMod - log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.") - } - } - - if shouldInstallDependencies { - if modMode == ModVendor { - log.Printf("Skipping dependency installation because a Go vendor directory was found.") - } else { - // automatically determine command to install dependencies - var install *exec.Cmd - if depMode == Dep { - // set up the dep cache if SEMMLE_CACHE is set - cacheDir := os.Getenv("SEMMLE_CACHE") - if cacheDir != "" { - depCacheDir := filepath.Join(cacheDir, "go", "dep") - log.Printf("Attempting to create dep cache dir %s\n", depCacheDir) - err := os.MkdirAll(depCacheDir, 0755) - if err != nil { - log.Printf("Failed to create dep cache directory: %s\n", err.Error()) - } else { - log.Printf("Setting dep cache directory to %s\n", depCacheDir) - err = os.Setenv("DEPCACHEDIR", depCacheDir) - if err != nil { - log.Println("Failed to set dep cache directory") - } else { - err = os.Setenv("DEPCACHEAGE", "720h") // 30 days - if err != nil { - log.Println("Failed to set dep cache age") - } - } - } - } - - if util.FileExists("Gopkg.lock") { - // if Gopkg.lock exists, don't update it and only vendor dependencies - install = exec.Command("dep", "ensure", "-v", "-vendor-only") - } else { - install = exec.Command("dep", "ensure", "-v") - } - log.Println("Installing dependencies using `dep ensure`.") - } else if depMode == Glide { - install = exec.Command("glide", "install") - log.Println("Installing dependencies using `glide install`") - } else { - // explicitly set go module support - if depMode == GoGetWithModules { - os.Setenv("GO111MODULE", "on") - } else if depMode == GoGetNoModules { - os.Setenv("GO111MODULE", "off") - } - - // get dependencies - install = exec.Command("go", "get", "-v", "./...") - log.Println("Installing dependencies using `go get -v ./...`.") - } - util.RunCmd(install) - } - } - - // extract - extractor, err := util.GetExtractorPath() - if err != nil { - log.Fatalf("Could not determine path of extractor: %v.\n", err) - } - - cwd, err := os.Getwd() - if err != nil { - log.Fatalf("Unable to determine current directory: %s\n", err.Error()) - } - - extractorArgs := []string{} - if depMode == GoGetWithModules { - extractorArgs = append(extractorArgs, modMode.argsForGoVersion(getEnvGoSemVer())...) - } - extractorArgs = append(extractorArgs, "./...") - - log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, cwd) - cmd := exec.Command(extractor, extractorArgs...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - if err != nil { - log.Fatalf("Extraction failed: %s\n", err.Error()) - } -} diff --git a/extractor/cli/go-autobuilder/go-autobuilder_test.go b/extractor/cli/go-autobuilder/go-autobuilder_test.go deleted file mode 100644 index f4e8405fe..000000000 --- a/extractor/cli/go-autobuilder/go-autobuilder_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import "testing" - -func TestGetImportPathFromRepoURL(t *testing.T) { - tests := map[string]string{ - "git@github.com:github/codeql-go.git": "github.com/github/codeql-go", - "git@github.com:github/codeql-go": "github.com/github/codeql-go", - "https://github.com/github/codeql-go.git": "github.com/github/codeql-go", - "https://github.com:12345/github/codeql-go": "github.com/github/codeql-go", - "gitolite@some.url:some/repo": "some.url/some/repo", - "file:///C:/some/path": "", - "https:///no/hostname": "", - "https://hostnameonly": "", - } - for input, expected := range tests { - actual := getImportPathFromRepoURL(input) - if actual != expected { - t.Errorf("Expected getImportPathFromRepoURL(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual) - } - } -} diff --git a/extractor/cli/go-bootstrap/go-bootstrap.go b/extractor/cli/go-bootstrap/go-bootstrap.go deleted file mode 100644 index 603da2b80..000000000 --- a/extractor/cli/go-bootstrap/go-bootstrap.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "regexp" -) - -// A utility program for generating `project` and `variable` files for SemmleCore Go projects -// -// This program should not normally be run directly; it is usually executed as part of -// `odasa bootstrap`, and expects two files as arguments: a (partial) `variables` file and -// an empty file to be filled in with an `` element containing build steps. -// -// The `variables` file is extended with a definition of `LGTM_SRC` and, if it defines the -// `repository` variable, `SEMMLE_REPO_URL`. The only build step is an invocation of the -// Go autobuilder. -func main() { - vars := os.Args[1] - buildSteps := os.Args[2] - - haveRepo := false - content, err := ioutil.ReadFile(vars) - if err != nil { - log.Fatal(err) - } - re := regexp.MustCompile(`(^|\n)repository=`) - haveRepo = re.Find(content) != nil - - additionalVars := "LGTM_SRC=${src}\n" - if haveRepo { - additionalVars += "SEMMLE_REPO_URL=${repository}\n" - } - content = append(content, []byte(additionalVars)...) - err = ioutil.WriteFile(vars, content, 0644) - if err != nil { - log.Fatal(err) - } - - export := "LGTM_SRC" - if haveRepo { - export += ",SEMMLE_REPO_URL" - } - content = []byte(fmt.Sprintf(` - ${semmle_dist}/language-packs/go/tools/platform/${semmle_platform}/bin/go-autobuilder - -`, export)) - err = ioutil.WriteFile(buildSteps, content, 0644) - if err != nil { - log.Fatal(err) - } -} diff --git a/extractor/cli/go-build-runner/go-build-runner.go b/extractor/cli/go-build-runner/go-build-runner.go deleted file mode 100644 index 118de5caf..000000000 --- a/extractor/cli/go-build-runner/go-build-runner.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "github.com/github/codeql-go/extractor/util" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - - "github.com/github/codeql-go/extractor/autobuilder" -) - -func main() { - // check if a build command has successfully extracted something - autobuilder.CheckExtracted = true - if autobuilder.Autobuild() { - return - } - - // if the autobuilder fails, invoke the extractor manually - // we cannot simply call `go build` here, because the tracer is not able to trace calls made by - // this binary - log.Printf("No build commands succeeded, falling back to go build ./...") - - mypath, err := os.Executable() - if err != nil { - log.Fatalf("Could not determine path of extractor: %v.\n", err) - } - extractor := filepath.Join(filepath.Dir(mypath), "go-extractor") - if runtime.GOOS == "windows" { - extractor = extractor + ".exe" - } - - util.RunCmd(exec.Command(extractor, "./...")) -} diff --git a/extractor/cli/go-extractor/go-extractor.go b/extractor/cli/go-extractor/go-extractor.go deleted file mode 100644 index 59ab35da4..000000000 --- a/extractor/cli/go-extractor/go-extractor.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - "runtime" - "runtime/pprof" - "strings" - - "github.com/github/codeql-go/extractor" -) - -var cpuprofile, memprofile string - -func usage() { - fmt.Fprintf(os.Stderr, "%s is a program for building a snapshot of a Go code base.\n\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "Usage:\n\n %s [...] [...] [--] ...\n\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "Flags:\n\n") - fmt.Fprintf(os.Stderr, "--help Print this help.\n") -} - -func parseFlags(args []string, mimic bool) ([]string, []string) { - i := 0 - buildFlags := []string{} - for ; i < len(args) && strings.HasPrefix(args[i], "-"); i++ { - if args[i] == "--" { - i++ - break - } - - if !mimic { - // we're not in mimic mode, try to parse our arguments - switch args[i] { - case "--help": - usage() - os.Exit(0) - case "--mimic": - if i+1 < len(args) { - i++ - compiler := args[i] - log.Printf("Compiler: %s", compiler) - if i+1 < len(args) { - i++ - command := args[i] - if command == "build" || command == "install" || command == "run" { - log.Printf("Intercepting build") - return parseFlags(args[i+1:], true) - } else { - log.Printf("Non-build command '%s'; skipping", strings.Join(args[1:], " ")) - os.Exit(0) - } - } else { - log.Printf("Non-build command '%s'; skipping", strings.Join(args[1:], " ")) - os.Exit(0) - } - } else { - log.Fatalf("--mimic requires an argument, e.g. --mimic go") - } - } - } - - // parse go build flags - switch args[i] { - // skip `-o output` and `-i`, if applicable - case "-o": - if i+1 < len(args) { - i++ - } - case "-i": - case "-p", "-asmflags", "-buildmode", "-compiler", "-gccgoflags", "-gcflags", "-installsuffix", - "-ldflags", "-mod", "-modfile", "-pkgdir", "-tags", "-toolexec", "-overlay": - if i+1 < len(args) { - buildFlags = append(buildFlags, args[i], args[i+1]) - i++ - } else { - buildFlags = append(buildFlags, args[i]) - } - default: - if strings.HasPrefix(args[i], "-") { - buildFlags = append(buildFlags, args[i]) - } else { - // stop parsing if the argument is not a flag (and so is positional) - break - } - } - } - - cpuprofile = os.Getenv("CODEQL_EXTRACTOR_GO_CPU_PROFILE") - memprofile = os.Getenv("CODEQL_EXTRACTOR_GO_MEM_PROFILE") - - return buildFlags, args[i:] -} - -func main() { - buildFlags, patterns := parseFlags(os.Args[1:], false) - - if cpuprofile != "" { - f, err := os.Create(cpuprofile) - if err != nil { - log.Fatalf("Unable to create CPU profile: %v.", err) - } - defer f.Close() - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatalf("Unable to start CPU profile: %v.", err) - } - defer pprof.StopCPUProfile() - } - - if len(patterns) == 0 { - log.Println("No packages explicitly provided, adding '.'") - patterns = []string{"."} - } - - log.Printf("Build flags: '%s'; patterns: '%s'\n", strings.Join(buildFlags, " "), strings.Join(patterns, " ")) - err := extractor.ExtractWithFlags(buildFlags, patterns) - if err != nil { - log.Fatalf("Error running go tooling: %s\n", err.Error()) - } - - if memprofile != "" { - f, err := os.Create(memprofile) - if err != nil { - log.Fatalf("Unable to create memory profile: %v", err) - } - defer f.Close() - runtime.GC() // get up-to-date statistics - if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatal("Unable to write memory profile: ", err) - } - } -} diff --git a/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go b/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go deleted file mode 100644 index 94831c2b4..000000000 --- a/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/github/codeql-go/extractor/dbscheme" -) - -func usage() { - fmt.Fprintf(os.Stderr, "%s is a program for generating the dbscheme for CodeQL Go databases.\n\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "Usage:\n\n %s \n\n", os.Args[0]) -} - -func main() { - if len(os.Args) != 2 { - usage() - os.Exit(2) - } - - out := os.Args[1] - - f, err := os.Create(out) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to open file %s for writing.\n", out) - os.Exit(1) - } - dbscheme.PrintDbScheme(f) - f.Close() - fmt.Printf("Dbscheme written to file %s.\n", out) -} diff --git a/extractor/cli/go-tokenizer/go-tokenizer.go b/extractor/cli/go-tokenizer/go-tokenizer.go deleted file mode 100644 index ec4a40571..000000000 --- a/extractor/cli/go-tokenizer/go-tokenizer.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "encoding/csv" - "flag" - "fmt" - "go/scanner" - "go/token" - "io/ioutil" - "log" - "os" - "strings" -) - -func main() { - flag.Parse() - - fs := token.NewFileSet() - csv := csv.NewWriter(os.Stdout) - defer csv.Flush() - - for _, fileName := range flag.Args() { - src, err := ioutil.ReadFile(fileName) - if err != nil { - log.Fatalf("Unable to read file %s.", fileName) - } - f := fs.AddFile(fileName, -1, len(src)) - - var s scanner.Scanner - s.Init(f, src, nil, 0) - for { - beginPos, tok, text := s.Scan() - - if strings.TrimSpace(text) != "" { - var fuzzyText string - if tok.IsLiteral() { - fuzzyText = tok.String() - } else { - fuzzyText = text - } - - endPos := f.Pos(f.Offset(beginPos) + len(text)) - beginLine := fmt.Sprintf("%d", f.Position(beginPos).Line) - beginColumn := fmt.Sprintf("%d", f.Position(beginPos).Column) - endLine := fmt.Sprintf("%d", f.Position(endPos).Line) - endColumn := fmt.Sprintf("%d", f.Position(endPos).Column) - err = csv.Write([]string{text, fuzzyText, beginLine, beginColumn, endLine, endColumn}) - if err != nil { - log.Fatalf("Unable to write CSV data: %v", err) - } - } - if tok == token.EOF { - break - } - } - } -} diff --git a/extractor/dbscheme/dbscheme.go b/extractor/dbscheme/dbscheme.go deleted file mode 100644 index 923fda859..000000000 --- a/extractor/dbscheme/dbscheme.go +++ /dev/null @@ -1,426 +0,0 @@ -package dbscheme - -import ( - "fmt" - "io" - "log" - "reflect" - "strings" - - "github.com/github/codeql-go/extractor/trap" -) - -// A Type represents a database type -type Type interface { - def() string - ref() string - repr() string - valid(val interface{}) bool -} - -// A PrimitiveType represents a primitive dataase type -type PrimitiveType int - -const ( - // INT represents the primitive database type `int` - INT PrimitiveType = iota - // FLOAT represents the primitive database type `float` - FLOAT - // BOOLEAN represents the primitive database type `boolean` - BOOLEAN - // DATE represents the primitive database type `date` - DATE - // STRING represents the primitive database type `string` - STRING -) - -// A PrimaryKeyType represents a database type defined by a primary key column -type PrimaryKeyType struct { - name string -} - -// A UnionType represents a database type defined as the union of other database types -type UnionType struct { - name string - components []Type -} - -// An AliasType represents a database type which is an alias of another database type -type AliasType struct { - name string - underlying Type -} - -// A CaseType represents a database type defined by a primary key column with a supplementary kind column -type CaseType struct { - base Type - column string - branches []*BranchType -} - -// A BranchType represents one branch of a case type -type BranchType struct { - idx int - name string -} - -func (pt PrimitiveType) def() string { - return "" -} - -func (pt PrimitiveType) ref() string { - switch pt { - case INT: - return "int" - case FLOAT: - return "float" - case BOOLEAN: - return "boolean" - case DATE: - return "date" - case STRING: - return "string" - default: - panic(fmt.Sprintf("Unexpected primitive type %d", pt)) - } -} - -func (pt PrimitiveType) repr() string { - switch pt { - case INT: - return "int" - case FLOAT: - return "float" - case BOOLEAN: - return "boolean" - case DATE: - return "date" - case STRING: - return "string" - default: - panic(fmt.Sprintf("Unexpected primitive type %d", pt)) - } -} - -func (pt PrimitiveType) valid(value interface{}) bool { - switch value.(type) { - case int: - return pt == INT - case float64: - return pt == FLOAT - case bool: - return pt == BOOLEAN - case string: - return pt == STRING - } - return false -} - -func (pkt PrimaryKeyType) def() string { - return "" -} - -func (pkt PrimaryKeyType) ref() string { - return pkt.name -} - -func (pkt PrimaryKeyType) repr() string { - return "int" -} - -func (pkt PrimaryKeyType) valid(value interface{}) bool { - _, ok := value.(trap.Label) - return ok -} - -func (ut UnionType) def() string { - var b strings.Builder - nl := 0 - fmt.Fprintf(&b, "%s = ", ut.name) - for i, comp := range ut.components { - if i > 0 { - if i < len(ut.components)-1 && b.Len()-nl > 100 { - fmt.Fprintf(&b, "\n%s", strings.Repeat(" ", len(ut.name))) - nl = b.Len() - } - fmt.Fprint(&b, " | ") - } - fmt.Fprint(&b, comp.ref()) - } - fmt.Fprint(&b, ";") - return b.String() -} - -func (ut UnionType) ref() string { - return ut.name -} - -func (ut UnionType) repr() string { - return "int" -} - -func (ut UnionType) valid(value interface{}) bool { - _, ok := value.(trap.Label) - return ok -} - -func (at AliasType) def() string { - return at.name + " = " + at.underlying.ref() + ";" -} - -func (at AliasType) ref() string { - return at.name -} - -func (at AliasType) repr() string { - return at.underlying.repr() -} - -func (at AliasType) valid(value interface{}) bool { - return at.underlying.valid(value) -} - -func (ct CaseType) def() string { - var b strings.Builder - fmt.Fprintf(&b, "case %s.%s of", ct.base.ref(), ct.column) - sep := " " - for _, branch := range ct.branches { - fmt.Fprintf(&b, "\n%s%s", sep, branch.def()) - sep = "| " - } - fmt.Fprint(&b, ";") - return b.String() -} - -func (ct CaseType) ref() string { - panic("case types do not have a name") -} - -func (ct CaseType) repr() string { - return "int" -} - -func (ct CaseType) valid(value interface{}) bool { - _, ok := value.(trap.Label) - return ok -} - -func (bt BranchType) def() string { - return fmt.Sprintf("%d = %s", bt.idx, bt.name) -} - -func (bt BranchType) ref() string { - return bt.name -} - -func (bt BranchType) repr() string { - return "int" -} - -func (bt BranchType) valid(value interface{}) bool { - _, ok := value.(trap.Label) - return ok -} - -// Index returns the numeric index of this branch type -func (bt BranchType) Index() int { - return bt.idx -} - -// A Column represents a column in a database table -type Column struct { - columnName string - columnType Type - unique bool - ref bool -} - -func (col Column) String() string { - var b strings.Builder - if col.unique { - fmt.Fprint(&b, "unique ") - } - fmt.Fprintf(&b, "%s %s: %s", col.columnType.repr(), col.columnName, col.columnType.ref()) - if col.ref { - fmt.Fprint(&b, " ref") - } - return b.String() -} - -// Key returns a new column that is the same as this column, but has the `key` flag set to `true` -func (col Column) Key() Column { - return Column{col.columnName, col.columnType, true, false} -} - -// Unique returns a new column that is the same as this column, but has the `unique` flag set to `true` -func (col Column) Unique() Column { - return Column{col.columnName, col.columnType, true, col.ref} -} - -// EntityColumn constructs a column with name `columnName` holding entities of type `columnType` -func EntityColumn(columnType Type, columnName string) Column { - return Column{columnName, columnType, false, true} -} - -// StringColumn constructs a column with name `columnName` holding string values -func StringColumn(columnName string) Column { - return Column{columnName, STRING, false, true} -} - -// IntColumn constructs a column with name `columnName` holding integer values -func IntColumn(columnName string) Column { - return Column{columnName, INT, false, true} -} - -// FloatColumn constructs a column with name `columnName` holding floating point number values -func FloatColumn(columnName string) Column { - return Column{columnName, FLOAT, false, true} -} - -// A Table represents a database table -type Table struct { - name string - schema []Column - keysets [][]string -} - -// KeySet adds `keys` as a keyset to this table -func (tbl *Table) KeySet(keys ...string) *Table { - tbl.keysets = append(tbl.keysets, keys) - return tbl -} - -func (tbl Table) String() string { - var b strings.Builder - for _, keyset := range tbl.keysets { - fmt.Fprint(&b, "#keyset[") - sep := "" - for _, key := range keyset { - fmt.Fprintf(&b, "%s%s", sep, key) - sep = ", " - } - fmt.Fprint(&b, "]\n") - } - fmt.Fprint(&b, tbl.name) - fmt.Fprint(&b, "(") - nl := 0 - for i, column := range tbl.schema { - if i > 0 { - // wrap >100 char lines - if i < len(tbl.schema)-1 && b.Len()-nl > 100 { - fmt.Fprintf(&b, ",\n%s", strings.Repeat(" ", len(tbl.name)+1)) - nl = b.Len() - } else { - fmt.Fprint(&b, ", ") - } - } - fmt.Fprint(&b, column.String()) - } - fmt.Fprint(&b, ");") - return b.String() -} - -// Emit outputs a tuple of `values` for this table using trap writer `tw` -// and panicks if the tuple does not have the right schema -func (tbl Table) Emit(tw *trap.Writer, values ...interface{}) { - if ncol, nval := len(tbl.schema), len(values); ncol != nval { - log.Fatalf("wrong number of values for table %s; expected %d, but got %d", tbl.name, ncol, nval) - } - for i, col := range tbl.schema { - if !col.columnType.valid(values[i]) { - panic(fmt.Sprintf("Invalid value for column %d of table %s; expected a %s, but got %s which is a %s", i, tbl.name, col.columnType.ref(), values[i], reflect.TypeOf(values[i]))) - } - } - tw.Emit(tbl.name, values) -} - -var tables = []*Table{} -var types = []Type{} -var defaultSnippets = []string{} - -// NewTable constructs a new table with the given `name` and `columns` -func NewTable(name string, columns ...Column) *Table { - tbl := &Table{name, columns, [][]string{}} - tables = append(tables, tbl) - return tbl -} - -// NewPrimaryKeyType constructs a new primary key type with the given `name`, -// and adds it to the union types `parents` (if any) -func NewPrimaryKeyType(name string, parents ...*UnionType) *PrimaryKeyType { - tp := &PrimaryKeyType{name} - types = append(types, tp) - for _, parent := range parents { - parent.components = append(parent.components, tp) - } - return tp -} - -// NewUnionType constructs a new union type with the given `name`, -// and adds it to the union types `parents` (if any) -func NewUnionType(name string, parents ...*UnionType) *UnionType { - tp := &UnionType{name, []Type{}} - types = append(types, tp) - for _, parent := range parents { - parent.components = append(parent.components, tp) - } - return tp -} - -// AddChild adds the type with given `name` to the union type. -// This is useful if a type defined in a snippet should be a child of a type defined in Go. -func (parent *UnionType) AddChild(name string) bool { - tp := &PrimaryKeyType{name} - // don't add tp to types; it's expected that it's already in the db somehow. - parent.components = append(parent.components, tp) - return true -} - -// NewAliasType constructs a new alias type with the given `name` that aliases `underlying` -func NewAliasType(name string, underlying Type) *AliasType { - tp := &AliasType{name, underlying} - types = append(types, tp) - return tp -} - -// NewCaseType constructs a new case type on the given `base` type whose discriminator values -// come from `column` -func NewCaseType(base Type, column string) *CaseType { - tp := &CaseType{base, column, []*BranchType{}} - types = append(types, tp) - return tp -} - -// NewBranch adds a new branch with the given `name` to this case type -// and adds it to the union types `parents` (if any) -func (ct *CaseType) NewBranch(name string, parents ...*UnionType) *BranchType { - tp := &BranchType{len(ct.branches), name} - ct.branches = append(ct.branches, tp) - for _, parent := range parents { - parent.components = append(parent.components, tp) - } - return tp -} - -// AddDefaultSnippet adds the given text `snippet` to the schema of this database -func AddDefaultSnippet(snippet string) bool { - defaultSnippets = append(defaultSnippets, snippet) - return true -} - -// PrintDbScheme prints the schema of this database to the writer `w` -func PrintDbScheme(w io.Writer) { - fmt.Fprintf(w, "/** Auto-generated dbscheme; do not edit. */\n\n") - for _, snippet := range defaultSnippets { - fmt.Fprintf(w, "%s\n", snippet) - } - for _, table := range tables { - fmt.Fprintf(w, "%s\n\n", table.String()) - } - for _, tp := range types { - def := tp.def() - if def != "" { - fmt.Fprintf(w, "%s\n\n", def) - } - } -} diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go deleted file mode 100644 index 63332aa3b..000000000 --- a/extractor/dbscheme/tables.go +++ /dev/null @@ -1,1222 +0,0 @@ -package dbscheme - -import ( - "go/ast" - "go/token" - gotypes "go/types" - - "golang.org/x/tools/go/packages" -) - -var defaultSnippet = AddDefaultSnippet(` -/** Duplicate code **/ - -duplicateCode( - unique int id : @duplication, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -similarCode( - unique int id : @similarity, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -@duplication_or_similarity = @duplication | @similarity; - -tokens( - int id : @duplication_or_similarity ref, - int offset : int ref, - int beginLine : int ref, - int beginColumn : int ref, - int endLine : int ref, - int endColumn : int ref); - -/** External data **/ - -externalData( - int id : @externalDataElement, - varchar(900) path : string ref, - int column: int ref, - varchar(900) value : string ref -); - -snapshotDate(unique date snapshotDate : date ref); - -sourceLocationPrefix(varchar(900) prefix : string ref); -`) - -// Copied directly from the XML dbscheme -var xmlSnippet = AddDefaultSnippet(` -/* - * XML Files - */ - -xmlEncoding( - unique int id: @file ref, - string encoding: string ref -); - -xmlDTDs( - unique int id: @xmldtd, - string root: string ref, - string publicId: string ref, - string systemId: string ref, - int fileid: @file ref -); - -xmlElements( - unique int id: @xmlelement, - string name: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int fileid: @file ref -); - -xmlAttrs( - unique int id: @xmlattribute, - int elementid: @xmlelement ref, - string name: string ref, - string value: string ref, - int idx: int ref, - int fileid: @file ref -); - -xmlNs( - int id: @xmlnamespace, - string prefixName: string ref, - string URI: string ref, - int fileid: @file ref -); - -xmlHasNs( - int elementId: @xmlnamespaceable ref, - int nsId: @xmlnamespace ref, - int fileid: @file ref -); - -xmlComments( - unique int id: @xmlcomment, - string text: string ref, - int parentid: @xmlparent ref, - int fileid: @file ref -); - -xmlChars( - unique int id: @xmlcharacters, - string text: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int isCDATA: int ref, - int fileid: @file ref -); - -@xmlparent = @file | @xmlelement; -@xmlnamespaceable = @xmlelement | @xmlattribute; - -xmllocations( - int xmlElement: @xmllocatable ref, - int location: @location_default ref -); - -@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; -`) - -// Compiler diagnostic tables -var CompilationType = NewPrimaryKeyType("@compilation") - -/** - * An invocation of the compiler. Note that more than one file may be - * compiled per invocation. For example, this command compiles three - * source files: - * - * go build a.go b.go c.go - * - * The `id` simply identifies the invocation, while `cwd` is the working - * directory from which the compiler was invoked. - */ -var CompilationsTable = NewTable("compilations", - EntityColumn(CompilationType, "id").Key(), - StringColumn("cwd"), -) - -/** - * The arguments that were passed to the extractor for a compiler - * invocation. If `id` is for the compiler invocation - * - * go build a.go b.go c.go - * - * then typically there will be rows for - * - * num | arg - * --- | --- - * 0 | *path to extractor* - * 1 | `--` - * 2 | a.go - * 3 | b.go - * 4 | c.go - */ -var CompilationArgsTable = NewTable("compilation_args", - EntityColumn(CompilationType, "id"), - IntColumn("num"), - StringColumn("arg"), -).KeySet("id", "num") - -/** - * The source files that are compiled by a compiler invocation. - * If `id` is for the compiler invocation - * - * go build a.go b.go c.go - * - * then there will be rows for - * - * num | arg - * --- | --- - * 0 | a.go - * 1 | b.go - * 2 | c.go - */ -var CompilationCompilingFilesTable = NewTable("compilation_compiling_files", - EntityColumn(CompilationType, "id"), - IntColumn("num"), - EntityColumn(FileType, "file"), -).KeySet("id", "num") - -type CompilationTypeKind int - -const ( - FRONTEND_CPU_SECONDS = iota - FRONTEND_ELAPSED_SECONDS - EXTRACTOR_CPU_SECONDS - EXTRACTOR_ELAPSED_SECONDS -) - -/** - * The time taken by the extractor for a compiler invocation. - * - * For each file `num`, there will be rows for - * - * kind | seconds - * ---- | --- - * 1 | CPU seconds used by the extractor frontend - * 2 | Elapsed seconds during the extractor frontend - * 3 | CPU seconds used by the extractor backend - * 4 | Elapsed seconds during the extractor backend - */ -var CompilationTimeTable = NewTable("compilation_time", - EntityColumn(CompilationType, "id"), - IntColumn("num"), - IntColumn("kind"), - FloatColumn("secs"), -).KeySet("id", "num", "kind") - -var DiagnosticType = NewPrimaryKeyType("@diagnostic") - -/** - * An error or warning generated by the extractor. - * The diagnostic message `diagnostic` was generated during compiler - * invocation `compilation`, and is the `file_number_diagnostic_number`th - * message generated while extracting the `file_number`th file of that - * invocation. - */ -var DiagnosticForTable = NewTable("diagnostic_for", - EntityColumn(DiagnosticType, "diagnostic").Unique(), - EntityColumn(CompilationType, "compilation"), - IntColumn("file_number"), - IntColumn("file_number_diagnostic_number"), -) - -/** - * If extraction was successful, then `cpu_seconds` and - * `elapsed_seconds` are the CPU time and elapsed time (respectively) - * that extraction took for compiler invocation `id`. - */ -var CompilationFinishedTable = NewTable("compilation_finished", - EntityColumn(CompilationType, "id").Unique(), - FloatColumn("cpu_seconds"), - FloatColumn("elapsed_seconds"), -) - -var DiagnosticsTable = NewTable("diagnostics", - EntityColumn(DiagnosticType, "id").Key(), - IntColumn("severity"), - StringColumn("error_tag"), - StringColumn("error_message"), - StringColumn("full_error_message"), - EntityColumn(LocationType, "location"), -) - -// ContainerType is the type of files and folders -var ContainerType = NewUnionType("@container") - -// LocatableType is the type of program entities that have locations -var LocatableType = NewUnionType("@locatable") - -// Adds xmllocatable as a locatable -var XmlLocatableAsLocatable = LocatableType.AddChild("@xmllocatable") - -// NodeType is the type of AST nodes -var NodeType = NewUnionType("@node", LocatableType) - -// DocumentableType is the type of AST nodes to which documentation can be attached -var DocumentableType = NewUnionType("@documentable", NodeType) - -// ExprParentType is the type of AST nodes that can have expressions as children -var ExprParentType = NewUnionType("@exprparent", NodeType) - -// ModExprParentType is the type of go.mod nodes that can have go.mod expressions as children -var ModExprParentType = NewUnionType("@modexprparent", NodeType) - -// FieldParentType is the type of AST nodes that can have fields as children -var FieldParentType = NewUnionType("@fieldparent", NodeType) - -// StmtParentType is the type of AST nodes that can have statements as children -var StmtParentType = NewUnionType("@stmtparent", NodeType) - -// DeclParentType is the type of AST nodes that can have declarations as children -var DeclParentType = NewUnionType("@declparent", NodeType) - -// TypeParamDeclParentType is the type of AST nodes that can have type parameter declarations as children -var TypeParamDeclParentType = NewUnionType("@typeparamdeclparent", NodeType) - -// FuncDefType is the type of AST nodes that define functions, that is, function -// declarations and function literals -var FuncDefType = NewUnionType("@funcdef", StmtParentType, ExprParentType) - -// ScopeNodeType is the type of AST nodes that may have a scope attached to them -var ScopeNodeType = NewUnionType("@scopenode", NodeType) - -// LocationDefaultType is the type of source locations -var LocationDefaultType = NewPrimaryKeyType("@location_default") - -// FileType is the type of file AST nodes -var FileType = NewPrimaryKeyType("@file", ContainerType, DocumentableType, ExprParentType, ModExprParentType, DeclParentType, ScopeNodeType) - -// FolderType is the type of folders -var FolderType = NewPrimaryKeyType("@folder", ContainerType) - -// CommentGroupType is the type of comment groups -var CommentGroupType = NewPrimaryKeyType("@comment_group", NodeType) - -// CommentType is the type of comments -var CommentType = NewPrimaryKeyType("@comment", NodeType) - -// ExprType is the type of expression AST nodes -var ExprType = NewPrimaryKeyType("@expr", ExprParentType) - -// FieldType is the type of field AST nodes -var FieldType = NewPrimaryKeyType("@field", DocumentableType, ExprParentType) - -// StmtType is the type of statement AST nodes -var StmtType = NewPrimaryKeyType("@stmt", ExprParentType, StmtParentType) - -// DeclType is the type of declaration AST nodes -var DeclType = NewPrimaryKeyType("@decl", ExprParentType, StmtParentType, FieldParentType) - -// TypeParamDeclType is the type of type parameter declaration AST nodes -var TypeParamDeclType = NewPrimaryKeyType("@typeparamdecl", DocumentableType, ExprParentType) - -// SpecType is the type of spec AST nodes -var SpecType = NewPrimaryKeyType("@spec", ExprParentType, DocumentableType) - -// TypeType is the type of types -var TypeType = NewPrimaryKeyType("@type") - -// LocationType is an alias for LocationDefaultType -var LocationType = NewAliasType("@location", LocationDefaultType) - -// SourceLineType is an alias for LocatableType -var SourceLineType = NewAliasType("@sourceline", LocatableType) - -// CommentKind is a case type for distinguishing different kinds of comments -var CommentKind = NewCaseType(CommentType, "kind") - -// SlashSlashComment is the type of single-line comments starting with a double slash -var SlashSlashComment = CommentKind.NewBranch("@slashslashcomment") - -// SlashStarComment is the type of block comments delimited by stars and slashes -var SlashStarComment = CommentKind.NewBranch("@slashstarcomment") - -// ExprKind is a case type for distinguishing different kinds of expression AST nodes -var ExprKind = NewCaseType(ExprType, "kind") - -// BadExpr is type of bad (that is, unparseable) expression AST nodes -var BadExpr = ExprKind.NewBranch("@badexpr") - -// IdentExpr is the type of identifier expression AST nodes -var IdentExpr = ExprKind.NewBranch("@ident") - -// EllipsisExpr is the type of ellipsis expression AST nodes -var EllipsisExpr = ExprKind.NewBranch("@ellipsis") - -// BasicLitExpr is the type of basic (that is, primitive) literal expression AST nodes -var BasicLitExpr = NewUnionType("@basiclit") - -// IntLitExpr is a case type for dishinguishing different kinds of literal expression AST nodes -var IntLitExpr = ExprKind.NewBranch("@intlit", BasicLitExpr) - -// FloatLitExpr is the type of floating-point literal expression AST nodes -var FloatLitExpr = ExprKind.NewBranch("@floatlit", BasicLitExpr) - -// ImagLitExpr is the type of imaginary literal expression AST nodes -var ImagLitExpr = ExprKind.NewBranch("@imaglit", BasicLitExpr) - -// CharLitExpr is the type of character literal expression AST nodes -var CharLitExpr = ExprKind.NewBranch("@charlit", BasicLitExpr) - -// StringLitExpr is the type of string literal expression AST nodes -var StringLitExpr = ExprKind.NewBranch("@stringlit", BasicLitExpr) - -// FuncLitExpr is the type of function literal expression AST nodes -var FuncLitExpr = ExprKind.NewBranch("@funclit", FuncDefType) - -// CompositeLitExpr is the type of composite literal expression AST nodes -var CompositeLitExpr = ExprKind.NewBranch("@compositelit") - -// ParenExpr is the type of parenthesis expression AST nodes -var ParenExpr = ExprKind.NewBranch("@parenexpr") - -// SelectorExpr is the type of selector expression AST nodes -var SelectorExpr = ExprKind.NewBranch("@selectorexpr") - -// IndexExpr is the type of AST nodes for index expressions and generic type -// instantiation expressions with one type argument. Note that syntactically -// unambiguous generic instantiations will be extracted as -// `GenericTypeInstantiationExpr`. -var IndexExpr = ExprKind.NewBranch("@indexexpr") - -// GenericFunctionInstantiationExpr is the type of AST nodes that represent an instantiation -// of a generic type. These correspond to some index expression AST nodes and all index -// list expression AST nodes. -var GenericFunctionInstantiationExpr = ExprKind.NewBranch("@genericfunctioninstantiationexpr") - -// GenericTypeInstantiationExpr is the type of AST nodes that represent an instantiation -// of a generic type. These correspond to some index expression AST nodes and all index -// list expression AST nodes. Note some syntactically ambiguous instantations are -// extracted as an `IndexExpr` to be disambiguated in QL later. -var GenericTypeInstantiationExpr = ExprKind.NewBranch("@generictypeinstantiationexpr") - -// SliceExpr is the type of slice expression AST nodes -var SliceExpr = ExprKind.NewBranch("@sliceexpr") - -// TypeAssertExpr is the type of type assertion expression AST nodes -var TypeAssertExpr = ExprKind.NewBranch("@typeassertexpr") - -// CallOrConversionExpr is the type of call and conversion expression AST nodes -// (which cannot be distinguished by purely syntactic criteria) -var CallOrConversionExpr = ExprKind.NewBranch("@callorconversionexpr") - -// StarExpr is the type of star expression AST nodes -var StarExpr = ExprKind.NewBranch("@starexpr") - -// OperatorExpr is the type of operator expression AST nodes -var OperatorExpr = NewUnionType("@operatorexpr") - -// LogicalExpr is the type of logical operator expression AST nodes -var LogicalExpr = NewUnionType("@logicalexpr", OperatorExpr) - -// ArithmeticExpr is the type of arithmetic operator expression AST nodes -var ArithmeticExpr = NewUnionType("@arithmeticexpr", OperatorExpr) - -// BitwiseExpr is the type of bitwise operator expression AST nodes -var BitwiseExpr = NewUnionType("@bitwiseexpr", OperatorExpr) - -// UnaryExpr is the type of unary operator expression AST nodes -var UnaryExpr = NewUnionType("@unaryexpr", OperatorExpr) - -// LogicalUnaryExpr is the type of logical unary operator expression AST nodes -var LogicalUnaryExpr = NewUnionType("@logicalunaryexpr", UnaryExpr, LogicalExpr) - -// BitwiseUnaryExpr is the type of bitwise unary operator expression AST nodes -var BitwiseUnaryExpr = NewUnionType("@bitwiseunaryexpr", UnaryExpr, BitwiseExpr) - -// ArithmeticUnaryExpr is the type of arithmetic unary operator expression AST nodes -var ArithmeticUnaryExpr = NewUnionType("@arithmeticunaryexpr", UnaryExpr, ArithmeticExpr) - -// BinaryExpr is the type of binary operator expression AST nodes -var BinaryExpr = NewUnionType("@binaryexpr", OperatorExpr) - -// LogicalBinaryExpr is the type of logical binary operator expression AST nodes -var LogicalBinaryExpr = NewUnionType("@logicalbinaryexpr", BinaryExpr, LogicalExpr) - -// BitwiseBinaryExpr is the type of bitwise binary operator expression AST nodes -var BitwiseBinaryExpr = NewUnionType("@bitwisebinaryexpr", BinaryExpr, BitwiseExpr) - -// ArithmeticBinaryExpr is the type of arithmetic binary operator expression AST nodes -var ArithmeticBinaryExpr = NewUnionType("@arithmeticbinaryexpr", BinaryExpr, ArithmeticExpr) - -// ShiftExpr is the type of shift operator expression AST nodes -var ShiftExpr = NewUnionType("@shiftexpr", BitwiseBinaryExpr) - -// Comparison is the type of comparison operator expression AST nodes -var Comparison = NewUnionType("@comparison", BinaryExpr) - -// EqualityTest is the type of equality operator expression AST nodes -var EqualityTest = NewUnionType("@equalitytest", Comparison) - -// RelationalComparison is the type of relational operator expression AST nodes -var RelationalComparison = NewUnionType("@relationalcomparison", Comparison) - -// KeyValueExpr is the type of key-value expression AST nodes -var KeyValueExpr = ExprKind.NewBranch("@keyvalueexpr") - -// ArrayTypeExpr is the type of array type AST nodes -var ArrayTypeExpr = ExprKind.NewBranch("@arraytypeexpr") - -// StructTypeExpr is the type of struct type AST nodes -var StructTypeExpr = ExprKind.NewBranch("@structtypeexpr", FieldParentType) - -// FuncTypeExpr is the type of function type AST nodes -var FuncTypeExpr = ExprKind.NewBranch("@functypeexpr", FieldParentType, ScopeNodeType) - -// InterfaceTypeExpr is the type of interface type AST nodes -var InterfaceTypeExpr = ExprKind.NewBranch("@interfacetypeexpr", FieldParentType) - -// MapTypeExpr is the type of map type AST nodes -var MapTypeExpr = ExprKind.NewBranch("@maptypeexpr") - -// TypeSetLiteralExpr is the type of type set literal type AST nodes -var TypeSetLiteralExpr = ExprKind.NewBranch("@typesetliteralexpr") - -// ChanTypeExpr is the type of channel type AST nodes -var ChanTypeExpr = NewUnionType("@chantypeexpr") - -// UnaryExprs is a map from unary operator tokens to the corresponding AST node type -var UnaryExprs = map[token.Token]*BranchType{ - token.ADD: ExprKind.NewBranch("@plusexpr", ArithmeticUnaryExpr), - token.SUB: ExprKind.NewBranch("@minusexpr", ArithmeticUnaryExpr), - token.NOT: ExprKind.NewBranch("@notexpr", LogicalUnaryExpr), - token.XOR: ExprKind.NewBranch("@complementexpr", BitwiseUnaryExpr), - token.MUL: ExprKind.NewBranch("@derefexpr", UnaryExpr), - token.AND: ExprKind.NewBranch("@addressexpr", UnaryExpr), - token.ARROW: ExprKind.NewBranch("@arrowexpr", UnaryExpr), -} - -// BinaryExprs is a map from binary operator tokens to the corresponding AST node type -var BinaryExprs = map[token.Token]*BranchType{ - token.LOR: ExprKind.NewBranch("@lorexpr", LogicalBinaryExpr), - token.LAND: ExprKind.NewBranch("@landexpr", LogicalBinaryExpr), - token.EQL: ExprKind.NewBranch("@eqlexpr", EqualityTest), - token.NEQ: ExprKind.NewBranch("@neqexpr", EqualityTest), - token.LSS: ExprKind.NewBranch("@lssexpr", RelationalComparison), - token.LEQ: ExprKind.NewBranch("@leqexpr", RelationalComparison), - token.GTR: ExprKind.NewBranch("@gtrexpr", RelationalComparison), - token.GEQ: ExprKind.NewBranch("@geqexpr", RelationalComparison), - token.ADD: ExprKind.NewBranch("@addexpr", ArithmeticBinaryExpr), - token.SUB: ExprKind.NewBranch("@subexpr", ArithmeticBinaryExpr), - token.OR: ExprKind.NewBranch("@orexpr", BitwiseBinaryExpr), - token.XOR: ExprKind.NewBranch("@xorexpr", BitwiseBinaryExpr), - token.MUL: ExprKind.NewBranch("@mulexpr", ArithmeticBinaryExpr), - token.QUO: ExprKind.NewBranch("@quoexpr", ArithmeticBinaryExpr), - token.REM: ExprKind.NewBranch("@remexpr", ArithmeticBinaryExpr), - token.SHL: ExprKind.NewBranch("@shlexpr", ShiftExpr), - token.SHR: ExprKind.NewBranch("@shrexpr", ShiftExpr), - token.AND: ExprKind.NewBranch("@andexpr", BitwiseBinaryExpr), - token.AND_NOT: ExprKind.NewBranch("@andnotexpr", BitwiseBinaryExpr), -} - -// ChanTypeExprs is a map from channel type expressions to the corresponding AST node type -var ChanTypeExprs = map[ast.ChanDir]*BranchType{ - ast.SEND: ExprKind.NewBranch("@sendchantypeexpr", ChanTypeExpr), - ast.RECV: ExprKind.NewBranch("@recvchantypeexpr", ChanTypeExpr), - ast.SEND | ast.RECV: ExprKind.NewBranch("@sendrcvchantypeexpr", ChanTypeExpr), -} - -// ErrorExpr is an AST node type that is not used anywhere -var ErrorExpr = ExprKind.NewBranch("@errorexpr") - -// StmtKind is a case type for distinguishing different kinds of statement AST nodes -var StmtKind = NewCaseType(StmtType, "kind") - -// BadStmtType is the type of bad (that is, unparseable) statement AST nodes -var BadStmtType = StmtKind.NewBranch("@badstmt") - -// DeclStmtType is the type of declaration statement AST nodes -var DeclStmtType = StmtKind.NewBranch("@declstmt", DeclParentType) - -// EmptyStmtType is the type of empty statement AST nodes -var EmptyStmtType = StmtKind.NewBranch("@emptystmt") - -// LabeledStmtType is the type of labeled statement AST nodes -var LabeledStmtType = StmtKind.NewBranch("@labeledstmt") - -// ExprStmtType is the type of expressio statemement AST nodes -var ExprStmtType = StmtKind.NewBranch("@exprstmt") - -// SendStmtType is the type of send statement AST nodes -var SendStmtType = StmtKind.NewBranch("@sendstmt") - -// IncDecStmtType is the type of increment/decrement statement AST nodes -var IncDecStmtType = NewUnionType("@incdecstmt") - -// IncStmtType is the type of increment statement AST nodes -var IncStmtType = StmtKind.NewBranch("@incstmt", IncDecStmtType) - -// DecStmtType is the type of decrement statement AST nodes -var DecStmtType = StmtKind.NewBranch("@decstmt", IncDecStmtType) - -// AssignmentType is the type of assignment statement AST nodes -var AssignmentType = NewUnionType("@assignment") - -// SimpleAssignStmtType is the type of simple (i.e., non-compound) assignment statement AST nodes -var SimpleAssignStmtType = NewUnionType("@simpleassignstmt", AssignmentType) - -// CompoundAssignStmtType is the type of compound assignment statement AST nodes -var CompoundAssignStmtType = NewUnionType("@compoundassignstmt", AssignmentType) - -// GoStmtType is the type of go statement AST nodes -var GoStmtType = StmtKind.NewBranch("@gostmt") - -// DeferStmtType is the type of defer statement AST nodes -var DeferStmtType = StmtKind.NewBranch("@deferstmt") - -// ReturnStmtType is the type of return statement AST nodes -var ReturnStmtType = StmtKind.NewBranch("@returnstmt") - -// BranchStmtType is the type of branch statement AST nodes -var BranchStmtType = NewUnionType("@branchstmt") - -// BreakStmtType is the type of break statement AST nodes -var BreakStmtType = StmtKind.NewBranch("@breakstmt", BranchStmtType) - -// ContinueStmtType is the type of continue statement AST nodes -var ContinueStmtType = StmtKind.NewBranch("@continuestmt", BranchStmtType) - -// GotoStmtType is the type of goto statement AST nodes -var GotoStmtType = StmtKind.NewBranch("@gotostmt", BranchStmtType) - -// FallthroughStmtType is the type of fallthrough statement AST nodes -var FallthroughStmtType = StmtKind.NewBranch("@fallthroughstmt", BranchStmtType) - -// BlockStmtType is the type of block statement AST nodes -var BlockStmtType = StmtKind.NewBranch("@blockstmt", ScopeNodeType) - -// IfStmtType is the type of if statement AST nodes -var IfStmtType = StmtKind.NewBranch("@ifstmt", ScopeNodeType) - -// CaseClauseType is the type of case clause AST nodes -var CaseClauseType = StmtKind.NewBranch("@caseclause", ScopeNodeType) - -// SwitchStmtType is the type of switch statement AST nodes, covering both expression switch and type switch -var SwitchStmtType = NewUnionType("@switchstmt", ScopeNodeType) - -// ExprSwitchStmtType is the type of expression-switch statement AST nodes -var ExprSwitchStmtType = StmtKind.NewBranch("@exprswitchstmt", SwitchStmtType) - -// TypeSwitchStmtType is the type of type-switch statement AST nodes -var TypeSwitchStmtType = StmtKind.NewBranch("@typeswitchstmt", SwitchStmtType) - -// CommClauseType is the type of comm clause AST ndoes -var CommClauseType = StmtKind.NewBranch("@commclause", ScopeNodeType) - -// SelectStmtType is the type of select statement AST nodes -var SelectStmtType = StmtKind.NewBranch("@selectstmt") - -// LoopStmtType is the type of loop statement AST nodes (including for statements and range statements) -var LoopStmtType = NewUnionType("@loopstmt", ScopeNodeType) - -// ForStmtType is the type of for statement AST nodes -var ForStmtType = StmtKind.NewBranch("@forstmt", LoopStmtType) - -// RangeStmtType is the type of range statement AST nodes -var RangeStmtType = StmtKind.NewBranch("@rangestmt", LoopStmtType) - -// AssignStmtTypes is a map from assignmnt operator tokens to corresponding AST node types -var AssignStmtTypes = map[token.Token]*BranchType{ - token.ASSIGN: StmtKind.NewBranch("@assignstmt", SimpleAssignStmtType), - token.DEFINE: StmtKind.NewBranch("@definestmt", SimpleAssignStmtType), - token.ADD_ASSIGN: StmtKind.NewBranch("@addassignstmt", CompoundAssignStmtType), - token.SUB_ASSIGN: StmtKind.NewBranch("@subassignstmt", CompoundAssignStmtType), - token.MUL_ASSIGN: StmtKind.NewBranch("@mulassignstmt", CompoundAssignStmtType), - token.QUO_ASSIGN: StmtKind.NewBranch("@quoassignstmt", CompoundAssignStmtType), - token.REM_ASSIGN: StmtKind.NewBranch("@remassignstmt", CompoundAssignStmtType), - token.AND_ASSIGN: StmtKind.NewBranch("@andassignstmt", CompoundAssignStmtType), - token.OR_ASSIGN: StmtKind.NewBranch("@orassignstmt", CompoundAssignStmtType), - token.XOR_ASSIGN: StmtKind.NewBranch("@xorassignstmt", CompoundAssignStmtType), - token.SHL_ASSIGN: StmtKind.NewBranch("@shlassignstmt", CompoundAssignStmtType), - token.SHR_ASSIGN: StmtKind.NewBranch("@shrassignstmt", CompoundAssignStmtType), - token.AND_NOT_ASSIGN: StmtKind.NewBranch("@andnotassignstmt", CompoundAssignStmtType), -} - -// DeclKind is a case type for distinguishing different kinds of declaration AST nodes -var DeclKind = NewCaseType(DeclType, "kind") - -// BadDeclType is the type of bad (that is, unparseable) declaration AST nodes -var BadDeclType = DeclKind.NewBranch("@baddecl") - -// GenDeclType is the type of generic declaration AST nodes -var GenDeclType = NewUnionType("@gendecl", DocumentableType) - -// ImportDeclType is the type of import declaration AST nodes -var ImportDeclType = DeclKind.NewBranch("@importdecl", GenDeclType) - -// ConstDeclType is the type of constant declaration AST nodes -var ConstDeclType = DeclKind.NewBranch("@constdecl", GenDeclType) - -// TypeDeclType is the type of type declaration AST nodes -var TypeDeclType = DeclKind.NewBranch("@typedecl", GenDeclType) - -// VarDeclType is the type of variable declaration AST nodes -var VarDeclType = DeclKind.NewBranch("@vardecl", GenDeclType) - -// FuncDeclType is the type of function declaration AST nodes -var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType, TypeParamDeclParentType) - -// SpecKind is a case type for distinguishing different kinds of declaration specification nodes -var SpecKind = NewCaseType(SpecType, "kind") - -// ImportSpecType is the type of import declaration specification nodes -var ImportSpecType = SpecKind.NewBranch("@importspec") - -// ValueSpecType is the type of value declaration specification nodes -var ValueSpecType = SpecKind.NewBranch("@valuespec") - -// TypeSpecType is the type of type declaration specification nodes -var TypeSpecType = NewUnionType("@typespec", TypeParamDeclParentType) - -// TypeDefSpecType is the type of type declaration specification nodes corresponding to type definitions -var TypeDefSpecType = SpecKind.NewBranch("@typedefspec", TypeSpecType) - -// AliasSpecType is the type of type declaration specification nodes corresponding to alias declarations -var AliasSpecType = SpecKind.NewBranch("@aliasspec", TypeSpecType) - -// ObjectType is the type of objects (that is, declared entities) -var ObjectType = NewPrimaryKeyType("@object") - -// ObjectKind is a case type for distinguishing different kinds of built-in and declared objects -var ObjectKind = NewCaseType(ObjectType, "kind") - -// TypeParamParentObjectType is the type of objects that can have type parameters as children -var TypeParamParentObjectType = NewUnionType("@typeparamparentobject") - -// DeclObjectType is the type of declared objects -var DeclObjectType = NewUnionType("@declobject") - -// BuiltinObjectType is the type of built-in objects -var BuiltinObjectType = NewUnionType("@builtinobject") - -// PkgObjectType is the type of imported packages -var PkgObjectType = ObjectKind.NewBranch("@pkgobject") - -// TypeObjectType is the type of declared or built-in named types -var TypeObjectType = NewUnionType("@typeobject") - -// DeclTypeObjectType is the type of declared named types -var DeclTypeObjectType = ObjectKind.NewBranch("@decltypeobject", TypeObjectType, DeclObjectType, TypeParamParentObjectType) - -// BuiltinTypeObjectType is the type of built-in named types -var BuiltinTypeObjectType = ObjectKind.NewBranch("@builtintypeobject", TypeObjectType, BuiltinObjectType) - -// ValueObjectType is the type of declared or built-in variables or constants -var ValueObjectType = NewUnionType("@valueobject") - -// ConstObjectType is the type of declared or built-in constants -var ConstObjectType = NewUnionType("@constobject", ValueObjectType) - -// DeclConstObjectType is the type of declared constants -var DeclConstObjectType = ObjectKind.NewBranch("@declconstobject", ConstObjectType, DeclObjectType) - -// BuiltinConstObjectType is the type of built-in constants -var BuiltinConstObjectType = ObjectKind.NewBranch("@builtinconstobject", ConstObjectType, BuiltinObjectType) - -// VarObjectType is the type of declared or built-in variables (the latter do not currently exist) -var VarObjectType = NewUnionType("@varobject", ValueObjectType) - -// DeclVarObjectType is the type of declared variables including function parameters, results and struct fields -var DeclVarObjectType = ObjectKind.NewBranch("@declvarobject", VarObjectType, DeclObjectType) - -// FunctionObjectType is the type of declared or built-in functions -var FunctionObjectType = NewUnionType("@functionobject", ValueObjectType) - -// DeclFuncObjectType is the type of declared functions, including (abstract and concrete) methods -var DeclFuncObjectType = ObjectKind.NewBranch("@declfunctionobject", FunctionObjectType, DeclObjectType, TypeParamParentObjectType) - -// BuiltinFuncObjectType is the type of built-in functions -var BuiltinFuncObjectType = ObjectKind.NewBranch("@builtinfunctionobject", FunctionObjectType, BuiltinObjectType) - -// LabelObjectType is the type of statement labels -var LabelObjectType = ObjectKind.NewBranch("@labelobject") - -// ScopeType is the type of scopes -var ScopeType = NewPrimaryKeyType("@scope") - -// ScopeKind is a case type for distinguishing different kinds of scopes -var ScopeKind = NewCaseType(ScopeType, "kind") - -// UniverseScopeType is the type of the universe scope -var UniverseScopeType = ScopeKind.NewBranch("@universescope") - -// PackageScopeType is the type of package scopes -var PackageScopeType = ScopeKind.NewBranch("@packagescope") - -// LocalScopeType is the type of local (that is, non-universe, non-package) scopes -var LocalScopeType = ScopeKind.NewBranch("@localscope", LocatableType) - -// TypeKind is a case type for distinguishing different kinds of types -var TypeKind = NewCaseType(TypeType, "kind") - -// BasicType is the union of all basic types -var BasicType = NewUnionType("@basictype") - -// BoolType is the union of the normal and literal bool types -var BoolType = NewUnionType("@booltype", BasicType) - -// NumericType is the union of numeric types -var NumericType = NewUnionType("@numerictype", BasicType) - -// IntegerType is the union of integer types -var IntegerType = NewUnionType("@integertype", NumericType) - -// SignedIntegerType is the union of signed integer types -var SignedIntegerType = NewUnionType("@signedintegertype", IntegerType) - -// UnsignedIntegerType is the union of unsigned integer types -var UnsignedIntegerType = NewUnionType("@unsignedintegertype", IntegerType) - -// FloatType is the union of floating-point types -var FloatType = NewUnionType("@floattype", NumericType) - -// ComplexType is the union of complex types -var ComplexType = NewUnionType("@complextype", NumericType) - -// StringType is the union of the normal and literal string types -var StringType = NewUnionType("@stringtype", BasicType) - -// LiteralType is the union of literal types -var LiteralType = NewUnionType("@literaltype", BasicType) - -// BasicTypes is a map from basic type kinds to the corresponding entity types -var BasicTypes = map[gotypes.BasicKind]*BranchType{ - gotypes.Invalid: TypeKind.NewBranch("@invalidtype", BasicType), - gotypes.Bool: TypeKind.NewBranch("@boolexprtype", BoolType), - gotypes.Int: TypeKind.NewBranch("@inttype", SignedIntegerType), - gotypes.Int8: TypeKind.NewBranch("@int8type", SignedIntegerType), - gotypes.Int16: TypeKind.NewBranch("@int16type", SignedIntegerType), - gotypes.Int32: TypeKind.NewBranch("@int32type", SignedIntegerType), - gotypes.Int64: TypeKind.NewBranch("@int64type", SignedIntegerType), - gotypes.Uint: TypeKind.NewBranch("@uinttype", UnsignedIntegerType), - gotypes.Uint8: TypeKind.NewBranch("@uint8type", UnsignedIntegerType), - gotypes.Uint16: TypeKind.NewBranch("@uint16type", UnsignedIntegerType), - gotypes.Uint32: TypeKind.NewBranch("@uint32type", UnsignedIntegerType), - gotypes.Uint64: TypeKind.NewBranch("@uint64type", UnsignedIntegerType), - gotypes.Uintptr: TypeKind.NewBranch("@uintptrtype", UnsignedIntegerType), - gotypes.Float32: TypeKind.NewBranch("@float32type", FloatType), - gotypes.Float64: TypeKind.NewBranch("@float64type", FloatType), - gotypes.Complex64: TypeKind.NewBranch("@complex64type", ComplexType), - gotypes.Complex128: TypeKind.NewBranch("@complex128type", ComplexType), - gotypes.String: TypeKind.NewBranch("@stringexprtype", StringType), - gotypes.UnsafePointer: TypeKind.NewBranch("@unsafepointertype", BasicType), - gotypes.UntypedBool: TypeKind.NewBranch("@boolliteraltype", LiteralType, BoolType), - gotypes.UntypedInt: TypeKind.NewBranch("@intliteraltype", LiteralType, SignedIntegerType), - gotypes.UntypedRune: TypeKind.NewBranch("@runeliteraltype", LiteralType, SignedIntegerType), - gotypes.UntypedFloat: TypeKind.NewBranch("@floatliteraltype", LiteralType, FloatType), - gotypes.UntypedComplex: TypeKind.NewBranch("@complexliteraltype", LiteralType, ComplexType), - gotypes.UntypedString: TypeKind.NewBranch("@stringliteraltype", LiteralType, StringType), - gotypes.UntypedNil: TypeKind.NewBranch("@nilliteraltype", LiteralType), -} - -// CompositeType is the type of all composite (that is, non-basic) types -var CompositeType = NewUnionType("@compositetype") - -// TypeParamType is the type of type parameter types -var TypeParamType = TypeKind.NewBranch("@typeparamtype", CompositeType) - -// ElementContainerType is the type of types that have elements, such as arrays -// and channels -var ElementContainerType = NewUnionType("@containertype", CompositeType) - -// ArrayType is the type of array types -var ArrayType = TypeKind.NewBranch("@arraytype", ElementContainerType) - -// SliceType is the type of slice types -var SliceType = TypeKind.NewBranch("@slicetype", ElementContainerType) - -// StructType is the type of struct types -var StructType = TypeKind.NewBranch("@structtype", CompositeType) - -// PointerType is the type of pointer types -var PointerType = TypeKind.NewBranch("@pointertype", CompositeType) - -// InterfaceType is the type of interface types -var InterfaceType = TypeKind.NewBranch("@interfacetype", CompositeType) - -// TupleType is the type of tuple types -var TupleType = TypeKind.NewBranch("@tupletype", CompositeType) - -// SignatureType is the type of signature types -var SignatureType = TypeKind.NewBranch("@signaturetype", CompositeType) - -// MapType is the type of map types -var MapType = TypeKind.NewBranch("@maptype", ElementContainerType) - -// ChanType is the type of channel types -var ChanType = NewUnionType("@chantype", ElementContainerType) - -// ChanTypes is a map from channel type directions to the corresponding type -var ChanTypes = map[gotypes.ChanDir]*BranchType{ - gotypes.SendOnly: TypeKind.NewBranch("@sendchantype", ChanType), - gotypes.RecvOnly: TypeKind.NewBranch("@recvchantype", ChanType), - gotypes.SendRecv: TypeKind.NewBranch("@sendrcvchantype", ChanType), -} - -// NamedType is the type of named types -var NamedType = TypeKind.NewBranch("@namedtype", CompositeType) - -// TypeSetLiteral is the type of type set literals -var TypeSetLiteral = TypeKind.NewBranch("@typesetliteraltype", CompositeType) - -// PackageType is the type of packages -var PackageType = NewPrimaryKeyType("@package") - -// ModExprType is the type of go.mod expression nodes -var ModExprType = NewPrimaryKeyType("@modexpr", ModExprParentType, DocumentableType) - -// ModExprKind is a case type for distinguishing different kinds of go.mod expression nodes -var ModExprKind = NewCaseType(ModExprType, "kind") - -// ModCommentBlockType is the type of go.mod comment block AST nodes -var ModCommentBlockType = ModExprKind.NewBranch("@modcommentblock") - -// ModLineType is the type of go.mod line AST nodes -var ModLineType = ModExprKind.NewBranch("@modline") - -// ModLineBlockType is the type of go.mod line block AST nodes -var ModLineBlockType = ModExprKind.NewBranch("@modlineblock") - -// ModLParenType is the type of go.mod line block start AST nodes -var ModLParenType = ModExprKind.NewBranch("@modlparen") - -// ModRParenType is the type of go.mod line block end AST nodes -var ModRParenType = ModExprKind.NewBranch("@modrparen") - -// ErrorType is the type of frontend errors -var ErrorType = NewPrimaryKeyType("@error") - -// ErrorKind is a case type for distinguishing different kinds of frontend errors -var ErrorKind = NewCaseType(ErrorType, "kind") - -// ErrorTypes is a map from error kinds to the corresponding type -var ErrorTypes = map[packages.ErrorKind]*BranchType{ - packages.UnknownError: ErrorKind.NewBranch("@unknownerror"), - packages.ListError: ErrorKind.NewBranch("@listerror"), - packages.ParseError: ErrorKind.NewBranch("@parseerror"), - packages.TypeError: ErrorKind.NewBranch("@typeerror"), -} - -// ErrorTypes is a map from error kinds to the corresponding tag -var ErrorTags = map[packages.ErrorKind]string{ - packages.UnknownError: "@unknownerror", - packages.ListError: "@listerror", - packages.ParseError: "@parseerror", - packages.TypeError: "@typeerror", -} - -// LocationsDefaultTable is the table defining location objects -var LocationsDefaultTable = NewTable("locations_default", - EntityColumn(LocationDefaultType, "id").Key(), - EntityColumn(FileType, "file"), - IntColumn("beginLine"), - IntColumn("beginColumn"), - IntColumn("endLine"), - IntColumn("endColumn"), -) - -// NumlinesTable is the table containing LoC information -var NumlinesTable = NewTable("numlines", - EntityColumn(SourceLineType, "element_id"), - IntColumn("num_lines"), - IntColumn("num_code"), - IntColumn("num_comment"), -) - -// FilesTable is the table defining file nodes -var FilesTable = NewTable("files", - EntityColumn(FileType, "id").Key(), - StringColumn("name"), -) - -// FoldersTable is the table defining folder entities -var FoldersTable = NewTable("folders", - EntityColumn(FolderType, "id").Key(), - StringColumn("name"), -) - -// ContainerParentTable is the table defining the parent-child relation among container entities -var ContainerParentTable = NewTable("containerparent", - EntityColumn(ContainerType, "parent"), - EntityColumn(ContainerType, "child").Unique(), -) - -// HasLocationTable is the table associating entities with their locations -var HasLocationTable = NewTable("has_location", - EntityColumn(LocatableType, "locatable").Unique(), - EntityColumn(LocationType, "location"), -) - -// CommentGroupsTable is the table defining comment group entities -var CommentGroupsTable = NewTable("comment_groups", - EntityColumn(CommentGroupType, "id").Key(), - EntityColumn(FileType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// CommentsTable is the table defining comment entities -var CommentsTable = NewTable("comments", - EntityColumn(CommentType, "id").Key(), - IntColumn("kind"), - EntityColumn(CommentGroupType, "parent"), - IntColumn("idx"), - StringColumn("text"), -) - -// DocCommentsTable is the table associating doc comments with the nodes they document -var DocCommentsTable = NewTable("doc_comments", - EntityColumn(DocumentableType, "node").Unique(), - EntityColumn(CommentGroupType, "comment"), -) - -// ExprsTable is the table defining expression AST nodes -var ExprsTable = NewTable("exprs", - EntityColumn(ExprType, "id").Key(), - IntColumn("kind"), - EntityColumn(ExprParentType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// LiteralsTable is the table associating literal expression AST nodes with their values -var LiteralsTable = NewTable("literals", - EntityColumn(ExprType, "expr").Unique(), - StringColumn("value"), - StringColumn("raw"), -) - -// ConstValuesTable is the table associating constant expressions with their values -var ConstValuesTable = NewTable("constvalues", - EntityColumn(ExprType, "expr").Unique(), - StringColumn("value"), - StringColumn("exact"), -) - -// FieldsTable is the table defining field AST nodes -var FieldsTable = NewTable("fields", - EntityColumn(FieldType, "id").Key(), - EntityColumn(FieldParentType, "parent"), - IntColumn("idx"), -) - -// TypeParamDeclsTable is the table defining type param declaration AST nodes -var TypeParamDeclsTable = NewTable("typeparamdecls", - EntityColumn(TypeParamDeclType, "id").Key(), - EntityColumn(TypeParamDeclParentType, "parent"), - IntColumn("idx"), -) - -// StmtsTable is the table defining statement AST nodes -var StmtsTable = NewTable("stmts", - EntityColumn(StmtType, "id").Key(), - IntColumn("kind"), - EntityColumn(StmtParentType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// DeclsTable is the table defining declaration AST nodes -var DeclsTable = NewTable("decls", - EntityColumn(DeclType, "id").Key(), - IntColumn("kind"), - EntityColumn(DeclParentType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// SpecsTable is the table defining declaration specification AST nodes -var SpecsTable = NewTable("specs", - EntityColumn(SpecType, "id").Key(), - IntColumn("kind"), - EntityColumn(GenDeclType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// ScopesTable is the table defining scopes -var ScopesTable = NewTable("scopes", - EntityColumn(ScopeType, "id").Key(), - IntColumn("kind"), -) - -// ScopeNestingTable is the table describing scope nesting -var ScopeNestingTable = NewTable("scopenesting", - EntityColumn(ScopeType, "inner").Unique(), - EntityColumn(ScopeType, "outer"), -) - -// ScopeNodesTable is the table associating local scopes with the AST nodes that induce them -var ScopeNodesTable = NewTable("scopenodes", - EntityColumn(ScopeNodeType, "node").Unique(), - EntityColumn(LocalScopeType, "scope"), -) - -// ObjectsTable is the table describing objects (that is, declared entities) -var ObjectsTable = NewTable("objects", - EntityColumn(ObjectType, "id").Key(), - IntColumn("kind"), - StringColumn("name"), -) - -// ObjectScopesTable is the table describing the scope to which an object belongs (if any) -var ObjectScopesTable = NewTable("objectscopes", - EntityColumn(ObjectType, "object").Unique(), - EntityColumn(ScopeType, "scope"), -) - -// ObjectTypesTable is the table describing the type of an object (if any) -var ObjectTypesTable = NewTable("objecttypes", - EntityColumn(ObjectType, "object").Unique(), - EntityColumn(TypeType, "tp"), -) - -// MethodReceiversTable maps methods to their receiver -var MethodReceiversTable = NewTable("methodreceivers", - EntityColumn(ObjectType, "method").Unique(), - EntityColumn(ObjectType, "receiver"), -) - -// FieldStructsTable maps fields to the structs they are in -var FieldStructsTable = NewTable("fieldstructs", - EntityColumn(ObjectType, "field").Unique(), - EntityColumn(StructType, "struct"), -) - -// MethodHostsTable maps interface methods to the named type they belong to -var MethodHostsTable = NewTable("methodhosts", - EntityColumn(ObjectType, "method"), - EntityColumn(NamedType, "host"), -) - -// DefsTable maps identifiers to the objects they define -var DefsTable = NewTable("defs", - EntityColumn(IdentExpr, "ident"), - EntityColumn(ObjectType, "object"), -) - -// UsesTable maps identifiers to the objects they denote -var UsesTable = NewTable("uses", - EntityColumn(IdentExpr, "ident"), - EntityColumn(ObjectType, "object"), -) - -// TypesTable is the table describing types -var TypesTable = NewTable("types", - EntityColumn(TypeType, "id").Key(), - IntColumn("kind"), -) - -// TypeOfTable is the table associating expressions with their types (if known) -var TypeOfTable = NewTable("type_of", - EntityColumn(ExprType, "expr").Unique(), - EntityColumn(TypeType, "tp"), -) - -// TypeNameTable is the table associating named types with their names -var TypeNameTable = NewTable("typename", - EntityColumn(TypeType, "tp").Unique(), - StringColumn("name"), -) - -// KeyTypeTable is the table associating maps with their key type -var KeyTypeTable = NewTable("key_type", - EntityColumn(MapType, "map").Unique(), - EntityColumn(TypeType, "tp"), -) - -// ElementTypeTable is the table associating container types with their element -// type -var ElementTypeTable = NewTable("element_type", - EntityColumn(ElementContainerType, "container").Unique(), - EntityColumn(TypeType, "tp"), -) - -// BaseTypeTable is the table associating pointer types with their base type -var BaseTypeTable = NewTable("base_type", - EntityColumn(PointerType, "ptr").Unique(), - EntityColumn(TypeType, "tp"), -) - -// UnderlyingTypeTable is the table associating named types with their -// underlying type -var UnderlyingTypeTable = NewTable("underlying_type", - EntityColumn(NamedType, "named").Unique(), - EntityColumn(TypeType, "tp"), -) - -// ComponentTypesTable is the table associating composite types with their component types -var ComponentTypesTable = NewTable("component_types", - EntityColumn(CompositeType, "parent"), - IntColumn("index"), - StringColumn("name"), - EntityColumn(TypeType, "tp"), -).KeySet("parent", "index") - -// ArrayLengthTable is the table associating array types with their length (represented as a string -// since Go array lengths are 64-bit and hence do not always fit into a QL integer) -var ArrayLengthTable = NewTable("array_length", - EntityColumn(ArrayType, "tp").Unique(), - StringColumn("len"), -) - -// TypeObjectTable maps types to their corresponding objects, if any -var TypeObjectTable = NewTable("type_objects", - EntityColumn(TypeType, "tp").Unique(), - EntityColumn(ObjectType, "object"), -) - -// PackagesTable is the table describing packages -var PackagesTable = NewTable("packages", - EntityColumn(PackageType, "id").Key(), - StringColumn("name"), - StringColumn("path"), - EntityColumn(PackageScopeType, "scope"), -) - -// ModExprsTable is the table defining expression AST nodes for go.mod files -var ModExprsTable = NewTable("modexprs", - EntityColumn(ModExprType, "id").Key(), - IntColumn("kind"), - EntityColumn(ModExprParentType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// ModTokensTable is the table associating go.mod tokens with their Line or LineBlock -var ModTokensTable = NewTable("modtokens", - StringColumn("token"), - EntityColumn(ModExprType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") - -// ErrorsTable is the table describing frontend errors -var ErrorsTable = NewTable("errors", - EntityColumn(ErrorType, "id").Key(), - IntColumn("kind"), - StringColumn("msg"), - StringColumn("rawpos"), - StringColumn("file"), - IntColumn("line"), - IntColumn("col"), - EntityColumn(PackageType, "package"), - IntColumn("idx"), -).KeySet("package", "idx") - -// HasEllipsisTable is the table containing all call expressions that have ellipses -var HasEllipsisTable = NewTable("has_ellipsis", - EntityColumn(CallOrConversionExpr, "id"), -) - -// VariadicTable is the table describing which functions are variadic -var VariadicTable = NewTable("variadic", - EntityColumn(SignatureType, "id"), -) - -// TypeParamTable is the table describing type parameter types -var TypeParamTable = NewTable("typeparam", - EntityColumn(TypeParamType, "tp").Unique(), - StringColumn("name"), - EntityColumn(CompositeType, "bound"), - EntityColumn(TypeParamParentObjectType, "parent"), - IntColumn("idx"), -).KeySet("parent", "idx") diff --git a/extractor/extractor.go b/extractor/extractor.go deleted file mode 100644 index 1be2bfef2..000000000 --- a/extractor/extractor.go +++ /dev/null @@ -1,2021 +0,0 @@ -package extractor - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "go/ast" - "go/constant" - "go/scanner" - "go/token" - "go/types" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "time" - - "github.com/github/codeql-go/extractor/dbscheme" - "github.com/github/codeql-go/extractor/srcarchive" - "github.com/github/codeql-go/extractor/trap" - "github.com/github/codeql-go/extractor/util" - "golang.org/x/tools/go/packages" -) - -var MaxGoRoutines int -var typeParamParent map[*types.TypeParam]types.Object = make(map[*types.TypeParam]types.Object) - -func init() { - // this sets the number of threads that the Go runtime will spawn; this is separate - // from the number of goroutines that the program spawns, which are scheduled into - // the system threads by the Go runtime scheduler - threads := os.Getenv("LGTM_THREADS") - if maxprocs, err := strconv.Atoi(threads); err == nil && maxprocs > 0 { - log.Printf("Max threads set to %d", maxprocs) - runtime.GOMAXPROCS(maxprocs) - } else if threads != "" { - log.Printf("Warning: LGTM_THREADS value %s is not valid, defaulting to using all available threads.", threads) - } - // if the value is empty or not set, use the Go default, which is the number of cores - // available since Go 1.5, but is subject to change - - var err error - if MaxGoRoutines, err = strconv.Atoi(util.Getenv( - "CODEQL_EXTRACTOR_GO_MAX_GOROUTINES", - "SEMMLE_MAX_GOROUTINES", - )); err != nil { - MaxGoRoutines = 32 - } else { - log.Printf("Max goroutines set to %d", MaxGoRoutines) - } -} - -// Extract extracts the packages specified by the given patterns -func Extract(patterns []string) error { - return ExtractWithFlags(nil, patterns) -} - -// ExtractWithFlags extracts the packages specified by the given patterns and build flags -func ExtractWithFlags(buildFlags []string, patterns []string) error { - startTime := time.Now() - - extraction := NewExtraction(buildFlags, patterns) - defer extraction.StatWriter.Close() - - modEnabled := os.Getenv("GO111MODULE") != "off" - if !modEnabled { - log.Println("Go module mode disabled.") - } - - modFlags := make([]string, 0, 1) - for _, flag := range buildFlags { - if strings.HasPrefix(flag, "-mod=") { - modFlags = append(modFlags, flag) - } - } - - log.Println("Running packages.Load.") - cfg := &packages.Config{ - Mode: packages.NeedName | packages.NeedFiles | - packages.NeedCompiledGoFiles | - packages.NeedImports | packages.NeedDeps | - packages.NeedTypes | packages.NeedTypesSizes | - packages.NeedTypesInfo | packages.NeedSyntax, - BuildFlags: buildFlags, - } - pkgs, err := packages.Load(cfg, patterns...) - if err != nil { - return err - } - log.Println("Done running packages.Load.") - - if len(pkgs) == 0 { - log.Println("No packages found.") - } - - log.Println("Extracting universe scope.") - extractUniverseScope() - log.Println("Done extracting universe scope.") - - // a map of package path to package root directory (currently the module root or the source directory) - pkgRoots := make(map[string]string) - // a map of package path to source code directory - pkgDirs := make(map[string]string) - // root directories of packages that we want to extract - wantedRoots := make(map[string]bool) - - // Do a post-order traversal and extract the package scope of each package - packages.Visit(pkgs, func(pkg *packages.Package) bool { - return true - }, func(pkg *packages.Package) { - log.Printf("Processing package %s.", pkg.PkgPath) - - if _, ok := pkgRoots[pkg.PkgPath]; !ok { - mdir := util.GetModDir(pkg.PkgPath, modFlags...) - pdir := util.GetPkgDir(pkg.PkgPath, modFlags...) - // GetModDir returns the empty string if the module directory cannot be determined, e.g. if the package - // is not using modules. If this is the case, fall back to the package directory - if mdir == "" { - mdir = pdir - } - pkgRoots[pkg.PkgPath] = mdir - pkgDirs[pkg.PkgPath] = pdir - } - - log.Printf("Extracting types for package %s.", pkg.PkgPath) - - tw, err := trap.NewWriter(pkg.PkgPath, pkg) - if err != nil { - log.Fatal(err) - } - defer tw.Close() - - scope := extractPackageScope(tw, pkg) - extractObjectTypes(tw) - lbl := tw.Labeler.GlobalID(util.EscapeTrapSpecialChars(pkg.PkgPath) + ";pkg") - dbscheme.PackagesTable.Emit(tw, lbl, pkg.Name, pkg.PkgPath, scope) - - if len(pkg.Errors) != 0 { - log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath) - for i, err := range pkg.Errors { - log.Printf(" %s", err.Error()) - extraction.extractError(tw, err, lbl, i) - } - } - log.Printf("Done extracting types for package %s.", pkg.PkgPath) - }) - - for _, pkg := range pkgs { - if pkgRoots[pkg.PkgPath] == "" { - log.Fatalf("Unable to get a source directory for input package %s.", pkg.PkgPath) - } - wantedRoots[pkgRoots[pkg.PkgPath]] = true - wantedRoots[pkgDirs[pkg.PkgPath]] = true - } - - log.Println("Done processing dependencies.") - - log.Println("Starting to extract packages.") - - sep := regexp.QuoteMeta(string(filepath.Separator)) - // if a path matches this regexp, we don't want to extract this package. Currently, it checks - // - that the path does not contain a `..` segment, and - // - the path does not contain a `vendor` directory. - noExtractRe := regexp.MustCompile(`.*(^|` + sep + `)(\.\.|vendor)($|` + sep + `).*`) - - // extract AST information for all packages - packages.Visit(pkgs, func(pkg *packages.Package) bool { - return true - }, func(pkg *packages.Package) { - for root, _ := range wantedRoots { - relDir, err := filepath.Rel(root, pkgDirs[pkg.PkgPath]) - if err != nil || noExtractRe.MatchString(relDir) { - // if the path can't be made relative or matches the noExtract regexp skip it - continue - } - - extraction.extractPackage(pkg) - - if pkgRoots[pkg.PkgPath] != "" { - modPath := filepath.Join(pkgRoots[pkg.PkgPath], "go.mod") - if util.FileExists(modPath) { - log.Printf("Extracting %s", modPath) - start := time.Now() - - err := extraction.extractGoMod(modPath) - if err != nil { - log.Printf("Failed to extract go.mod: %s", err.Error()) - } - - end := time.Since(start) - log.Printf("Done extracting %s (%dms)", modPath, end.Nanoseconds()/1000000) - } - } - - return - } - - log.Printf("Skipping dependency package %s.", pkg.PkgPath) - }) - - extraction.WaitGroup.Wait() - - log.Println("Done extracting packages.") - - t := time.Now() - elapsed := t.Sub(startTime) - dbscheme.CompilationFinishedTable.Emit(extraction.StatWriter, extraction.Label, 0.0, elapsed.Seconds()) - - return nil -} - -type Extraction struct { - // A lock for preventing concurrent writes to maps and the stat trap writer, as they are not - // thread-safe - Lock sync.Mutex - LabelKey string - Label trap.Label - StatWriter *trap.Writer - WaitGroup sync.WaitGroup - GoroutineSem *semaphore - FdSem *semaphore - NextFileId int - FileInfo map[string]*FileInfo - SeenGoMods map[string]bool -} - -type FileInfo struct { - Idx int - NextErr int -} - -func (extraction *Extraction) SeenFile(path string) bool { - _, ok := extraction.FileInfo[path] - return ok -} - -func (extraction *Extraction) GetFileInfo(path string) *FileInfo { - if fileInfo, ok := extraction.FileInfo[path]; ok { - return fileInfo - } - - extraction.FileInfo[path] = &FileInfo{extraction.NextFileId, 0} - extraction.NextFileId += 1 - - return extraction.FileInfo[path] -} - -func (extraction *Extraction) GetFileIdx(path string) int { - return extraction.GetFileInfo(path).Idx -} - -func (extraction *Extraction) GetNextErr(path string) int { - finfo := extraction.GetFileInfo(path) - res := finfo.NextErr - finfo.NextErr += 1 - return res -} - -func NewExtraction(buildFlags []string, patterns []string) *Extraction { - hash := md5.New() - io.WriteString(hash, "go") - for _, buildFlag := range buildFlags { - io.WriteString(hash, " "+buildFlag) - } - io.WriteString(hash, " --") - for _, pattern := range patterns { - io.WriteString(hash, " "+pattern) - } - sum := hash.Sum(nil) - - i := 0 - var path string - // split compilation files into directories to avoid filling a single directory with too many files - pathFmt := fmt.Sprintf("compilations/%s/%s_%%d", hex.EncodeToString(sum[:1]), hex.EncodeToString(sum[1:])) - for { - path = fmt.Sprintf(pathFmt, i) - file, err := trap.FileFor(path) - if err != nil { - log.Fatalf("Error creating trap file: %s\n", err.Error()) - } - i++ - - if !util.FileExists(file) { - break - } - } - - statWriter, err := trap.NewWriter(path, nil) - if err != nil { - log.Fatal(err) - } - lblKey := fmt.Sprintf("%s_%d;compilation", hex.EncodeToString(sum), i) - lbl := statWriter.Labeler.GlobalID(lblKey) - - wd, err := os.Getwd() - if err != nil { - log.Fatalf("Unable to determine current directory: %s\n", err.Error()) - } - - dbscheme.CompilationsTable.Emit(statWriter, lbl, wd) - i = 0 - extractorPath, err := util.GetExtractorPath() - if err != nil { - log.Fatalf("Unable to get extractor path: %s\n", err.Error()) - } - dbscheme.CompilationArgsTable.Emit(statWriter, lbl, 0, extractorPath) - i++ - for _, flag := range buildFlags { - dbscheme.CompilationArgsTable.Emit(statWriter, lbl, i, flag) - i++ - } - // emit a fake "--" argument to make it clear that what comes after it are patterns - dbscheme.CompilationArgsTable.Emit(statWriter, lbl, i, "--") - i++ - for _, pattern := range patterns { - dbscheme.CompilationArgsTable.Emit(statWriter, lbl, i, pattern) - i++ - } - - return &Extraction{ - LabelKey: lblKey, - Label: lbl, - StatWriter: statWriter, - // this semaphore is used to limit the number of files that are open at once; - // this is to prevent the extractor from running into issues with caps on the - // number of open files that can be held by one process - FdSem: newSemaphore(100), - // this semaphore is used to limit the number of goroutines spawned, so we - // don't run into memory issues - GoroutineSem: newSemaphore(MaxGoRoutines), - NextFileId: 0, - FileInfo: make(map[string]*FileInfo), - SeenGoMods: make(map[string]bool), - } -} - -// extractUniverseScope extracts symbol table information for the universe scope -func extractUniverseScope() { - tw, err := trap.NewWriter("universe", nil) - if err != nil { - log.Fatal(err) - } - defer tw.Close() - - lbl := tw.Labeler.ScopeID(types.Universe, nil) - dbscheme.ScopesTable.Emit(tw, lbl, dbscheme.UniverseScopeType.Index()) - extractObjects(tw, types.Universe, lbl) - - // Always extract an empty interface type - extractType(tw, types.NewInterfaceType([]*types.Func{}, []types.Type{})) -} - -// extractObjects extracts all objects declared in the given scope -func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) { - for _, name := range scope.Names() { - obj := scope.Lookup(name) - lbl, exists := tw.Labeler.ScopedObjectID(obj, func() trap.Label { return extractType(tw, obj.Type()) }) - if !exists { - // Populate type parameter parents for functions. Note that methods - // do not appear as objects in any scope, so they have to be dealt - // with separately in extractMethods. - if funcObj, ok := obj.(*types.Func); ok { - populateTypeParamParents(tw, funcObj.Type().(*types.Signature).TypeParams(), obj) - populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), obj) - } - // Populate type parameter parents for named types. Note that we - // skip type aliases as the original type should be the parent - // of any type parameters. - if typeNameObj, ok := obj.(*types.TypeName); ok && !typeNameObj.IsAlias() { - if tp, ok := typeNameObj.Type().(*types.Named); ok { - populateTypeParamParents(tw, tp.TypeParams(), obj) - } - } - extractObject(tw, obj, lbl) - } - - if obj.Parent() != scope { - // this can happen if a scope is embedded into another with a `.` import. - continue - } - dbscheme.ObjectScopesTable.Emit(tw, lbl, scopeLabel) - } -} - -// extractMethod extracts a method `meth` and emits it to the objects table, then returns its label -func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label { - // get the receiver type of the method - recvtyp := meth.Type().(*types.Signature).Recv().Type() - // ensure receiver type has been extracted - recvtyplbl := extractType(tw, recvtyp) - - // if the method label does not exist, extract it - methlbl, exists := tw.Labeler.MethodID(meth, recvtyplbl) - if !exists { - // Populate type parameter parents for methods. They do not appear as - // objects in any scope, so they have to be dealt with separately here. - populateTypeParamParents(tw, meth.Type().(*types.Signature).TypeParams(), meth) - populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), meth) - extractObject(tw, meth, methlbl) - } - - return methlbl -} - -// extractObject extracts a single object and emits it to the objects table. -func extractObject(tw *trap.Writer, obj types.Object, lbl trap.Label) { - name := obj.Name() - isBuiltin := obj.Parent() == types.Universe - var kind int - switch obj.(type) { - case *types.PkgName: - kind = dbscheme.PkgObjectType.Index() - case *types.TypeName: - if isBuiltin { - kind = dbscheme.BuiltinTypeObjectType.Index() - } else { - kind = dbscheme.DeclTypeObjectType.Index() - } - case *types.Const: - if isBuiltin { - kind = dbscheme.BuiltinConstObjectType.Index() - } else { - kind = dbscheme.DeclConstObjectType.Index() - } - case *types.Nil: - kind = dbscheme.BuiltinConstObjectType.Index() - case *types.Var: - kind = dbscheme.DeclVarObjectType.Index() - case *types.Builtin: - kind = dbscheme.BuiltinFuncObjectType.Index() - case *types.Func: - kind = dbscheme.DeclFuncObjectType.Index() - case *types.Label: - kind = dbscheme.LabelObjectType.Index() - default: - log.Fatalf("unknown object of type %T", obj) - } - dbscheme.ObjectsTable.Emit(tw, lbl, kind, name) - - // for methods, additionally extract information about the receiver - if sig, ok := obj.Type().(*types.Signature); ok { - if recv := sig.Recv(); recv != nil { - recvlbl, exists := tw.Labeler.ReceiverObjectID(recv, lbl) - if !exists { - extractObject(tw, recv, recvlbl) - } - dbscheme.MethodReceiversTable.Emit(tw, lbl, recvlbl) - } - } -} - -// extractObjectTypes extracts type and receiver information for all objects -func extractObjectTypes(tw *trap.Writer) { - // calling `extractType` on a named type will extract all methods defined - // on it, which will add new objects. Therefore we need to do this first - // before we loops over all objects and emit them. - changed := true - for changed { - changed = tw.ForEachObject(extractObjectType) - } - changed = tw.ForEachObject(emitObjectType) - if changed { - log.Printf("Warning: more objects were labeled while emitted object types") - } -} - -// extractObjectType extracts type and receiver information for a given object -func extractObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) { - if tp := obj.Type(); tp != nil { - extractType(tw, tp) - } -} - -// emitObjectType emits the type information for a given object -func emitObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) { - if tp := obj.Type(); tp != nil { - dbscheme.ObjectTypesTable.Emit(tw, lbl, extractType(tw, tp)) - } -} - -var ( - // file:line:col - threePartPos = regexp.MustCompile(`^(.+):(\d+):(\d+)$`) - // file:line - twoPartPos = regexp.MustCompile(`^(.+):(\d+)$`) -) - -// extractError extracts the message and location of a frontend error -func (extraction *Extraction) extractError(tw *trap.Writer, err packages.Error, pkglbl trap.Label, idx int) { - var ( - lbl = tw.Labeler.FreshID() - tag = dbscheme.ErrorTags[err.Kind] - kind = dbscheme.ErrorTypes[err.Kind].Index() - pos = err.Pos - file = "" - line, col int - e error - ) - - if pos == "" || pos == "-" { - // extract a dummy file - wd, e := os.Getwd() - if e != nil { - wd = "." - log.Printf("Warning: failed to get working directory") - } - ewd, e := filepath.EvalSymlinks(wd) - if e != nil { - ewd = wd - log.Printf("Warning: failed to evaluate symlinks for %s", wd) - } - file = filepath.Join(ewd, "-") - } else { - var rawfile string - if parts := threePartPos.FindStringSubmatch(pos); parts != nil { - // "file:line:col" - col, e = strconv.Atoi(parts[3]) - if e != nil { - log.Printf("Warning: malformed column number `%s`: %v", parts[3], e) - } - line, e = strconv.Atoi(parts[2]) - if e != nil { - log.Printf("Warning: malformed line number `%s`: %v", parts[2], e) - } - rawfile = parts[1] - } else if parts := twoPartPos.FindStringSubmatch(pos); parts != nil { - // "file:line" - line, e = strconv.Atoi(parts[2]) - if e != nil { - log.Printf("Warning: malformed line number `%s`: %v", parts[2], e) - } - rawfile = parts[1] - } else if pos != "" && pos != "-" { - log.Printf("Warning: malformed error position `%s`", pos) - } - afile, e := filepath.Abs(rawfile) - if e != nil { - log.Printf("Warning: failed to get absolute path for for %s", file) - afile = file - } - file, e = filepath.EvalSymlinks(afile) - if e != nil { - log.Printf("Warning: failed to evaluate symlinks for %s", afile) - file = afile - } - - extraction.extractFileInfo(tw, file) - } - - extraction.Lock.Lock() - flbl := extraction.StatWriter.Labeler.FileLabelFor(file) - diagLbl := extraction.StatWriter.Labeler.FreshID() - dbscheme.DiagnosticsTable.Emit( - extraction.StatWriter, diagLbl, 1, tag, err.Msg, err.Msg, - emitLocation(extraction.StatWriter, flbl, line, col, line, col)) - dbscheme.DiagnosticForTable.Emit(extraction.StatWriter, diagLbl, extraction.Label, extraction.GetFileIdx(file), extraction.GetNextErr(file)) - extraction.Lock.Unlock() - transformed := filepath.ToSlash(srcarchive.TransformPath(file)) - dbscheme.ErrorsTable.Emit(tw, lbl, kind, err.Msg, pos, transformed, line, col, pkglbl, idx) -} - -// extractPackage extracts AST information for all files in the given package -func (extraction *Extraction) extractPackage(pkg *packages.Package) { - for _, astFile := range pkg.Syntax { - extraction.WaitGroup.Add(1) - extraction.GoroutineSem.acquire(1) - go func(astFile *ast.File) { - err := extraction.extractFile(astFile, pkg) - if err != nil { - log.Fatal(err) - } - extraction.GoroutineSem.release(1) - extraction.WaitGroup.Done() - }(astFile) - } -} - -// normalizedPath computes the normalized path (with symlinks resolved) for the given file -func normalizedPath(ast *ast.File, fset *token.FileSet) string { - file := fset.File(ast.Package).Name() - path, err := filepath.EvalSymlinks(file) - if err != nil { - return file - } - return path -} - -// extractFile extracts AST information for the given file -func (extraction *Extraction) extractFile(ast *ast.File, pkg *packages.Package) error { - fset := pkg.Fset - if ast.Package == token.NoPos { - log.Printf("Skipping extracting a file without a 'package' declaration") - return nil - } - path := normalizedPath(ast, fset) - - extraction.FdSem.acquire(3) - - log.Printf("Extracting %s", path) - start := time.Now() - - defer extraction.FdSem.release(1) - tw, err := trap.NewWriter(path, pkg) - if err != nil { - extraction.FdSem.release(2) - return err - } - defer tw.Close() - - err = srcarchive.Add(path) - extraction.FdSem.release(2) - if err != nil { - return err - } - - extraction.extractFileInfo(tw, path) - - extractScopes(tw, ast, pkg) - - extractFileNode(tw, ast) - - extractObjectTypes(tw) - - extractNumLines(tw, path, ast) - - end := time.Since(start) - log.Printf("Done extracting %s (%dms)", path, end.Nanoseconds()/1000000) - - return nil -} - -// extractFileInfo extracts file-system level information for the given file, populating -// the `files` and `containerparent` tables -func (extraction *Extraction) extractFileInfo(tw *trap.Writer, file string) { - // We may visit the same file twice because `extractError` calls this function to describe files containing - // compilation errors. It is also called for user source files being extracted. - extraction.Lock.Lock() - if extraction.SeenFile(file) { - extraction.Lock.Unlock() - return - } - extraction.Lock.Unlock() - - path := filepath.ToSlash(srcarchive.TransformPath(file)) - components := strings.Split(path, "/") - parentPath := "" - var parentLbl trap.Label - - for i, component := range components { - if i == 0 { - if component == "" { - path = "/" - } else { - path = component - } - } else { - path = parentPath + "/" + component - } - if i == len(components)-1 { - lbl := tw.Labeler.FileLabelFor(file) - dbscheme.FilesTable.Emit(tw, lbl, path) - dbscheme.ContainerParentTable.Emit(tw, parentLbl, lbl) - dbscheme.HasLocationTable.Emit(tw, lbl, emitLocation(tw, lbl, 0, 0, 0, 0)) - extraction.Lock.Lock() - slbl := extraction.StatWriter.Labeler.FileLabelFor(file) - dbscheme.CompilationCompilingFilesTable.Emit(extraction.StatWriter, extraction.Label, extraction.GetFileIdx(file), slbl) - extraction.Lock.Unlock() - break - } - lbl := tw.Labeler.GlobalID(util.EscapeTrapSpecialChars(path) + ";folder") - dbscheme.FoldersTable.Emit(tw, lbl, path) - if i > 0 { - dbscheme.ContainerParentTable.Emit(tw, parentLbl, lbl) - } - if path != "/" { - parentPath = path - } - parentLbl = lbl - } -} - -// extractLocation emits a location entity for the given entity -func extractLocation(tw *trap.Writer, entity trap.Label, sl int, sc int, el int, ec int) { - filelbl := tw.Labeler.FileLabel() - dbscheme.HasLocationTable.Emit(tw, entity, emitLocation(tw, filelbl, sl, sc, el, ec)) -} - -// emitLocation emits a location entity -func emitLocation(tw *trap.Writer, filelbl trap.Label, sl int, sc int, el int, ec int) trap.Label { - locLbl := tw.Labeler.GlobalID(fmt.Sprintf("loc,{%s},%d,%d,%d,%d", filelbl, sl, sc, el, ec)) - dbscheme.LocationsDefaultTable.Emit(tw, locLbl, filelbl, sl, sc, el, ec) - - return locLbl -} - -// extractNodeLocation extracts location information for the given node -func extractNodeLocation(tw *trap.Writer, nd ast.Node, lbl trap.Label) { - if nd == nil { - return - } - fset := tw.Package.Fset - start, end := fset.Position(nd.Pos()), fset.Position(nd.End()) - extractLocation(tw, lbl, start.Line, start.Column, end.Line, end.Column-1) -} - -// extractPackageScope extracts symbol table information for the given package -func extractPackageScope(tw *trap.Writer, pkg *packages.Package) trap.Label { - pkgScope := pkg.Types.Scope() - pkgScopeLabel := tw.Labeler.ScopeID(pkgScope, pkg.Types) - dbscheme.ScopesTable.Emit(tw, pkgScopeLabel, dbscheme.PackageScopeType.Index()) - dbscheme.ScopeNestingTable.Emit(tw, pkgScopeLabel, tw.Labeler.ScopeID(types.Universe, nil)) - extractObjects(tw, pkgScope, pkgScopeLabel) - return pkgScopeLabel -} - -// extractScopeLocation extracts location information for the given scope -func extractScopeLocation(tw *trap.Writer, scope *types.Scope, lbl trap.Label) { - fset := tw.Package.Fset - start, end := fset.Position(scope.Pos()), fset.Position(scope.End()) - extractLocation(tw, lbl, start.Line, start.Column, end.Line, end.Column-1) -} - -// extractScopes extracts symbol table information for the package scope and all local scopes -// of the given package. Note that this will not encounter methods or struct fields as -// they do not have a parent scope. -func extractScopes(tw *trap.Writer, nd *ast.File, pkg *packages.Package) { - pkgScopeLabel := extractPackageScope(tw, pkg) - fileScope := pkg.TypesInfo.Scopes[nd] - if fileScope != nil { - extractLocalScope(tw, fileScope, pkgScopeLabel) - } -} - -// extractLocalScope extracts symbol table information for the given scope and all its nested scopes -func extractLocalScope(tw *trap.Writer, scope *types.Scope, parentScopeLabel trap.Label) { - scopeLabel := tw.Labeler.ScopeID(scope, nil) - dbscheme.ScopesTable.Emit(tw, scopeLabel, dbscheme.LocalScopeType.Index()) - extractScopeLocation(tw, scope, scopeLabel) - dbscheme.ScopeNestingTable.Emit(tw, scopeLabel, parentScopeLabel) - - for i := 0; i < scope.NumChildren(); i++ { - childScope := scope.Child(i) - extractLocalScope(tw, childScope, scopeLabel) - } - - extractObjects(tw, scope, scopeLabel) -} - -// extractFileNode extracts AST information for the given file and all nodes contained in it -func extractFileNode(tw *trap.Writer, nd *ast.File) { - lbl := tw.Labeler.FileLabel() - - extractExpr(tw, nd.Name, lbl, 0) - - for i, decl := range nd.Decls { - extractDecl(tw, decl, lbl, i) - } - - for i, cg := range nd.Comments { - extractCommentGroup(tw, cg, lbl, i) - } - - extractDoc(tw, nd.Doc, lbl) - emitScopeNodeInfo(tw, nd, lbl) -} - -// extractDoc extracts information about a doc comment group associated with a given element -func extractDoc(tw *trap.Writer, doc *ast.CommentGroup, elt trap.Label) { - if doc != nil { - dbscheme.DocCommentsTable.Emit(tw, elt, tw.Labeler.LocalID(doc)) - } -} - -// extractCommentGroup extracts information about a doc comment group -func extractCommentGroup(tw *trap.Writer, cg *ast.CommentGroup, parent trap.Label, idx int) { - lbl := tw.Labeler.LocalID(cg) - dbscheme.CommentGroupsTable.Emit(tw, lbl, parent, idx) - extractNodeLocation(tw, cg, lbl) - for i, c := range cg.List { - extractComment(tw, c, lbl, i) - } -} - -// extractComment extracts information about a given comment -func extractComment(tw *trap.Writer, c *ast.Comment, parent trap.Label, idx int) { - lbl := tw.Labeler.LocalID(c) - rawText := c.Text - var kind int - var text string - if rawText[:2] == "//" { - kind = dbscheme.SlashSlashComment.Index() - text = rawText[2:] - } else { - kind = dbscheme.SlashStarComment.Index() - text = rawText[2 : len(rawText)-2] - } - dbscheme.CommentsTable.Emit(tw, lbl, kind, parent, idx, text) - extractNodeLocation(tw, c, lbl) -} - -// emitScopeNodeInfo associates an AST node with its induced scope, if any -func emitScopeNodeInfo(tw *trap.Writer, nd ast.Node, lbl trap.Label) { - scope, exists := tw.Package.TypesInfo.Scopes[nd] - if exists { - dbscheme.ScopeNodesTable.Emit(tw, lbl, tw.Labeler.ScopeID(scope, tw.Package.Types)) - } -} - -// extractExpr extracts AST information for the given expression and all its subexpressions -func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { - if expr == nil { - return - } - - lbl := tw.Labeler.LocalID(expr) - extractTypeOf(tw, expr, lbl) - - var kind int - switch expr := expr.(type) { - case *ast.BadExpr: - kind = dbscheme.BadExpr.Index() - case *ast.Ident: - if expr == nil { - return - } - kind = dbscheme.IdentExpr.Index() - dbscheme.LiteralsTable.Emit(tw, lbl, expr.Name, expr.Name) - def := tw.Package.TypesInfo.Defs[expr] - if def != nil { - defTyp := extractType(tw, def.Type()) - objlbl, exists := tw.Labeler.LookupObjectID(def, defTyp) - if objlbl == trap.InvalidLabel { - log.Printf("Omitting def binding to unknown object %v", def) - } else { - if !exists { - extractObject(tw, def, objlbl) - } - dbscheme.DefsTable.Emit(tw, lbl, objlbl) - } - } - use := getObjectBeingUsed(tw, expr) - if use != nil { - useTyp := extractType(tw, use.Type()) - objlbl, exists := tw.Labeler.LookupObjectID(use, useTyp) - if objlbl == trap.InvalidLabel { - log.Printf("Omitting use binding to unknown object %v", use) - } else { - if !exists { - extractObject(tw, use, objlbl) - } - dbscheme.UsesTable.Emit(tw, lbl, objlbl) - } - } - case *ast.Ellipsis: - if expr == nil { - return - } - kind = dbscheme.EllipsisExpr.Index() - extractExpr(tw, expr.Elt, lbl, 0) - case *ast.BasicLit: - if expr == nil { - return - } - value := "" - switch expr.Kind { - case token.INT: - ival, _ := strconv.ParseInt(expr.Value, 0, 64) - value = strconv.FormatInt(ival, 10) - kind = dbscheme.IntLitExpr.Index() - case token.FLOAT: - value = expr.Value - kind = dbscheme.FloatLitExpr.Index() - case token.IMAG: - value = expr.Value - kind = dbscheme.ImagLitExpr.Index() - case token.CHAR: - value, _ = strconv.Unquote(expr.Value) - kind = dbscheme.CharLitExpr.Index() - case token.STRING: - value, _ = strconv.Unquote(expr.Value) - kind = dbscheme.StringLitExpr.Index() - default: - log.Fatalf("unknown literal kind %v", expr.Kind) - } - dbscheme.LiteralsTable.Emit(tw, lbl, value, expr.Value) - case *ast.FuncLit: - if expr == nil { - return - } - kind = dbscheme.FuncLitExpr.Index() - extractExpr(tw, expr.Type, lbl, 0) - extractStmt(tw, expr.Body, lbl, 1) - case *ast.CompositeLit: - if expr == nil { - return - } - kind = dbscheme.CompositeLitExpr.Index() - extractExpr(tw, expr.Type, lbl, 0) - extractExprs(tw, expr.Elts, lbl, 1, 1) - case *ast.ParenExpr: - if expr == nil { - return - } - kind = dbscheme.ParenExpr.Index() - extractExpr(tw, expr.X, lbl, 0) - case *ast.SelectorExpr: - if expr == nil { - return - } - kind = dbscheme.SelectorExpr.Index() - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Sel, lbl, 1) - case *ast.IndexExpr: - if expr == nil { - return - } - typeofx := typeOf(tw, expr.X) - if typeofx == nil { - // We are missing type information for `expr.X`, so we cannot - // determine whether this is a generic function instantiation - // or not. - kind = dbscheme.IndexExpr.Index() - } else { - if _, ok := typeofx.Underlying().(*types.Signature); ok { - kind = dbscheme.GenericFunctionInstantiationExpr.Index() - } else { - // Can't distinguish between actual index expressions (into a - // map, array, slice, string or pointer to array) and generic - // type specialization expression, so we do it later in QL. - kind = dbscheme.IndexExpr.Index() - } - } - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Index, lbl, 1) - case *ast.IndexListExpr: - if expr == nil { - return - } - typeofx := typeOf(tw, expr.X) - if typeofx == nil { - // We are missing type information for `expr.X`, so we cannot - // determine whether this is a generic function instantiation - // or not. - kind = dbscheme.GenericTypeInstantiationExpr.Index() - } else { - if _, ok := typeofx.Underlying().(*types.Signature); ok { - kind = dbscheme.GenericFunctionInstantiationExpr.Index() - } else { - kind = dbscheme.GenericTypeInstantiationExpr.Index() - } - } - extractExpr(tw, expr.X, lbl, 0) - extractExprs(tw, expr.Indices, lbl, 1, 1) - case *ast.SliceExpr: - if expr == nil { - return - } - kind = dbscheme.SliceExpr.Index() - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Low, lbl, 1) - extractExpr(tw, expr.High, lbl, 2) - extractExpr(tw, expr.Max, lbl, 3) - case *ast.TypeAssertExpr: - if expr == nil { - return - } - kind = dbscheme.TypeAssertExpr.Index() - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Type, lbl, 1) - case *ast.CallExpr: - if expr == nil { - return - } - kind = dbscheme.CallOrConversionExpr.Index() - extractExpr(tw, expr.Fun, lbl, 0) - extractExprs(tw, expr.Args, lbl, 1, 1) - if expr.Ellipsis.IsValid() { - dbscheme.HasEllipsisTable.Emit(tw, lbl) - } - case *ast.StarExpr: - if expr == nil { - return - } - kind = dbscheme.StarExpr.Index() - extractExpr(tw, expr.X, lbl, 0) - case *ast.KeyValueExpr: - if expr == nil { - return - } - kind = dbscheme.KeyValueExpr.Index() - extractExpr(tw, expr.Key, lbl, 0) - extractExpr(tw, expr.Value, lbl, 1) - case *ast.UnaryExpr: - if expr == nil { - return - } - if expr.Op == token.TILDE { - kind = dbscheme.TypeSetLiteralExpr.Index() - } else { - tp := dbscheme.UnaryExprs[expr.Op] - if tp == nil { - log.Fatalf("unsupported unary operator %s", expr.Op) - } - kind = tp.Index() - } - extractExpr(tw, expr.X, lbl, 0) - case *ast.BinaryExpr: - if expr == nil { - return - } - _, isUnionType := typeOf(tw, expr).(*types.Union) - if expr.Op == token.OR && isUnionType { - kind = dbscheme.TypeSetLiteralExpr.Index() - flattenBinaryExprTree(tw, expr, lbl, 0) - } else { - tp := dbscheme.BinaryExprs[expr.Op] - if tp == nil { - log.Fatalf("unsupported binary operator %s", expr.Op) - } - kind = tp.Index() - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Y, lbl, 1) - } - case *ast.ArrayType: - if expr == nil { - return - } - kind = dbscheme.ArrayTypeExpr.Index() - extractExpr(tw, expr.Len, lbl, 0) - extractExpr(tw, expr.Elt, lbl, 1) - case *ast.StructType: - if expr == nil { - return - } - kind = dbscheme.StructTypeExpr.Index() - extractFields(tw, expr.Fields, lbl, 0, 1) - case *ast.FuncType: - if expr == nil { - return - } - kind = dbscheme.FuncTypeExpr.Index() - extractFields(tw, expr.Params, lbl, 0, 1) - extractFields(tw, expr.Results, lbl, -1, -1) - emitScopeNodeInfo(tw, expr, lbl) - case *ast.InterfaceType: - if expr == nil { - return - } - kind = dbscheme.InterfaceTypeExpr.Index() - // expr.Methods contains methods, embedded interfaces and type set - // literals. - makeTypeSetLiteralsUnionTyped(tw, expr.Methods) - extractFields(tw, expr.Methods, lbl, 0, 1) - case *ast.MapType: - if expr == nil { - return - } - kind = dbscheme.MapTypeExpr.Index() - extractExpr(tw, expr.Key, lbl, 0) - extractExpr(tw, expr.Value, lbl, 1) - case *ast.ChanType: - if expr == nil { - return - } - tp := dbscheme.ChanTypeExprs[expr.Dir] - if tp == nil { - log.Fatalf("unsupported channel direction %v", expr.Dir) - } - kind = tp.Index() - extractExpr(tw, expr.Value, lbl, 0) - default: - log.Fatalf("unknown expression of type %T", expr) - } - dbscheme.ExprsTable.Emit(tw, lbl, kind, parent, idx) - extractNodeLocation(tw, expr, lbl) - extractValueOf(tw, expr, lbl) -} - -// extractExprs extracts AST information for a list of expressions, which are children of -// the given parent -// `idx` is the index of the first child in the list, and `dir` is the index increment of -// each child over its preceding child (usually either 1 for assigning increasing indices, or -// -1 for decreasing indices) -func extractExprs(tw *trap.Writer, exprs []ast.Expr, parent trap.Label, idx int, dir int) { - if exprs != nil { - for _, expr := range exprs { - extractExpr(tw, expr, parent, idx) - idx += dir - } - } -} - -// extractTypeOf looks up the type of `expr`, extracts it if it hasn't previously been -// extracted, and associates it with `expr` in the `type_of` table -func extractTypeOf(tw *trap.Writer, expr ast.Expr, lbl trap.Label) { - tp := typeOf(tw, expr) - if tp != nil { - tplbl := extractType(tw, tp) - dbscheme.TypeOfTable.Emit(tw, lbl, tplbl) - } -} - -// extractValueOf looks up the value of `expr`, and associates it with `expr` in -// the `consts` table -func extractValueOf(tw *trap.Writer, expr ast.Expr, lbl trap.Label) { - tpVal := tw.Package.TypesInfo.Types[expr] - - if tpVal.Value != nil { - // if Value is non-nil, the expression has a constant value - - // note that string literals in import statements do not have an associated - // Value and so do not get added to the table - - var value string - exact := tpVal.Value.ExactString() - switch tpVal.Value.Kind() { - case constant.String: - // we need to unquote strings - value = constant.StringVal(tpVal.Value) - exact = constant.StringVal(tpVal.Value) - case constant.Float: - flval, _ := constant.Float64Val(tpVal.Value) - value = fmt.Sprintf("%.20g", flval) - case constant.Complex: - real, _ := constant.Float64Val(constant.Real(tpVal.Value)) - imag, _ := constant.Float64Val(constant.Imag(tpVal.Value)) - value = fmt.Sprintf("(%.20g + %.20gi)", real, imag) - default: - value = tpVal.Value.ExactString() - } - - dbscheme.ConstValuesTable.Emit(tw, lbl, value, exact) - } else if tpVal.IsNil() { - dbscheme.ConstValuesTable.Emit(tw, lbl, "nil", "nil") - } -} - -// extractFields extracts AST information for a list of fields, which are children of -// the given parent -// `idx` is the index of the first child in the list, and `dir` is the index increment of -// each child over its preceding child (usually either 1 for assigning increasing indices, or -// -1 for decreasing indices) -func extractFields(tw *trap.Writer, fields *ast.FieldList, parent trap.Label, idx int, dir int) { - if fields == nil || fields.List == nil { - return - } - for _, field := range fields.List { - lbl := tw.Labeler.LocalID(field) - dbscheme.FieldsTable.Emit(tw, lbl, parent, idx) - extractNodeLocation(tw, field, lbl) - if field.Names != nil { - for i, name := range field.Names { - extractExpr(tw, name, lbl, i+1) - } - } - extractExpr(tw, field.Type, lbl, 0) - extractExpr(tw, field.Tag, lbl, -1) - extractDoc(tw, field.Doc, lbl) - idx += dir - } -} - -// extractStmt extracts AST information for a given statement and all other statements or expressions -// nested inside it -func extractStmt(tw *trap.Writer, stmt ast.Stmt, parent trap.Label, idx int) { - if stmt == nil { - return - } - - lbl := tw.Labeler.LocalID(stmt) - var kind int - switch stmt := stmt.(type) { - case *ast.BadStmt: - kind = dbscheme.BadStmtType.Index() - case *ast.DeclStmt: - if stmt == nil { - return - } - kind = dbscheme.DeclStmtType.Index() - extractDecl(tw, stmt.Decl, lbl, 0) - case *ast.EmptyStmt: - kind = dbscheme.EmptyStmtType.Index() - case *ast.LabeledStmt: - if stmt == nil { - return - } - kind = dbscheme.LabeledStmtType.Index() - extractExpr(tw, stmt.Label, lbl, 0) - extractStmt(tw, stmt.Stmt, lbl, 1) - case *ast.ExprStmt: - if stmt == nil { - return - } - kind = dbscheme.ExprStmtType.Index() - extractExpr(tw, stmt.X, lbl, 0) - case *ast.SendStmt: - if stmt == nil { - return - } - kind = dbscheme.SendStmtType.Index() - extractExpr(tw, stmt.Chan, lbl, 0) - extractExpr(tw, stmt.Value, lbl, 1) - case *ast.IncDecStmt: - if stmt == nil { - return - } - if stmt.Tok == token.INC { - kind = dbscheme.IncStmtType.Index() - } else if stmt.Tok == token.DEC { - kind = dbscheme.DecStmtType.Index() - } else { - log.Fatalf("unsupported increment/decrement operator %v", stmt.Tok) - } - extractExpr(tw, stmt.X, lbl, 0) - case *ast.AssignStmt: - if stmt == nil { - return - } - tp := dbscheme.AssignStmtTypes[stmt.Tok] - if tp == nil { - log.Fatalf("unsupported assignment statement with operator %v", stmt.Tok) - } - kind = tp.Index() - extractExprs(tw, stmt.Lhs, lbl, -1, -1) - extractExprs(tw, stmt.Rhs, lbl, 1, 1) - case *ast.GoStmt: - if stmt == nil { - return - } - kind = dbscheme.GoStmtType.Index() - extractExpr(tw, stmt.Call, lbl, 0) - case *ast.DeferStmt: - if stmt == nil { - return - } - kind = dbscheme.DeferStmtType.Index() - extractExpr(tw, stmt.Call, lbl, 0) - case *ast.ReturnStmt: - kind = dbscheme.ReturnStmtType.Index() - extractExprs(tw, stmt.Results, lbl, 0, 1) - case *ast.BranchStmt: - if stmt == nil { - return - } - switch stmt.Tok { - case token.BREAK: - kind = dbscheme.BreakStmtType.Index() - case token.CONTINUE: - kind = dbscheme.ContinueStmtType.Index() - case token.GOTO: - kind = dbscheme.GotoStmtType.Index() - case token.FALLTHROUGH: - kind = dbscheme.FallthroughStmtType.Index() - default: - log.Fatalf("unsupported branch statement type %v", stmt.Tok) - } - extractExpr(tw, stmt.Label, lbl, 0) - case *ast.BlockStmt: - if stmt == nil { - return - } - kind = dbscheme.BlockStmtType.Index() - extractStmts(tw, stmt.List, lbl, 0, 1) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.IfStmt: - if stmt == nil { - return - } - kind = dbscheme.IfStmtType.Index() - extractStmt(tw, stmt.Init, lbl, 0) - extractExpr(tw, stmt.Cond, lbl, 1) - extractStmt(tw, stmt.Body, lbl, 2) - extractStmt(tw, stmt.Else, lbl, 3) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.CaseClause: - if stmt == nil { - return - } - kind = dbscheme.CaseClauseType.Index() - extractExprs(tw, stmt.List, lbl, -1, -1) - extractStmts(tw, stmt.Body, lbl, 0, 1) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.SwitchStmt: - if stmt == nil { - return - } - kind = dbscheme.ExprSwitchStmtType.Index() - extractStmt(tw, stmt.Init, lbl, 0) - extractExpr(tw, stmt.Tag, lbl, 1) - extractStmt(tw, stmt.Body, lbl, 2) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.TypeSwitchStmt: - if stmt == nil { - return - } - kind = dbscheme.TypeSwitchStmtType.Index() - extractStmt(tw, stmt.Init, lbl, 0) - extractStmt(tw, stmt.Assign, lbl, 1) - extractStmt(tw, stmt.Body, lbl, 2) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.CommClause: - if stmt == nil { - return - } - kind = dbscheme.CommClauseType.Index() - extractStmt(tw, stmt.Comm, lbl, 0) - extractStmts(tw, stmt.Body, lbl, 1, 1) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.SelectStmt: - kind = dbscheme.SelectStmtType.Index() - extractStmt(tw, stmt.Body, lbl, 0) - case *ast.ForStmt: - if stmt == nil { - return - } - kind = dbscheme.ForStmtType.Index() - extractStmt(tw, stmt.Init, lbl, 0) - extractExpr(tw, stmt.Cond, lbl, 1) - extractStmt(tw, stmt.Post, lbl, 2) - extractStmt(tw, stmt.Body, lbl, 3) - emitScopeNodeInfo(tw, stmt, lbl) - case *ast.RangeStmt: - if stmt == nil { - return - } - kind = dbscheme.RangeStmtType.Index() - extractExpr(tw, stmt.Key, lbl, 0) - extractExpr(tw, stmt.Value, lbl, 1) - extractExpr(tw, stmt.X, lbl, 2) - extractStmt(tw, stmt.Body, lbl, 3) - emitScopeNodeInfo(tw, stmt, lbl) - default: - log.Fatalf("unknown statement of type %T", stmt) - } - dbscheme.StmtsTable.Emit(tw, lbl, kind, parent, idx) - extractNodeLocation(tw, stmt, lbl) -} - -// extractStmts extracts AST information for a list of statements, which are children of -// the given parent -// `idx` is the index of the first child in the list, and `dir` is the index increment of -// each child over its preceding child (usually either 1 for assigning increasing indices, or -// -1 for decreasing indices) -func extractStmts(tw *trap.Writer, stmts []ast.Stmt, parent trap.Label, idx int, dir int) { - if stmts != nil { - for _, stmt := range stmts { - extractStmt(tw, stmt, parent, idx) - idx += dir - } - } - -} - -// extractDecl extracts AST information for the given declaration -func extractDecl(tw *trap.Writer, decl ast.Decl, parent trap.Label, idx int) { - lbl := tw.Labeler.LocalID(decl) - var kind int - switch decl := decl.(type) { - case *ast.BadDecl: - kind = dbscheme.BadDeclType.Index() - case *ast.GenDecl: - if decl == nil { - return - } - switch decl.Tok { - case token.IMPORT: - kind = dbscheme.ImportDeclType.Index() - case token.CONST: - kind = dbscheme.ConstDeclType.Index() - case token.TYPE: - kind = dbscheme.TypeDeclType.Index() - case token.VAR: - kind = dbscheme.VarDeclType.Index() - default: - log.Fatalf("unknown declaration of kind %v", decl.Tok) - } - for i, spec := range decl.Specs { - extractSpec(tw, spec, lbl, i) - } - extractDoc(tw, decl.Doc, lbl) - case *ast.FuncDecl: - if decl == nil { - return - } - kind = dbscheme.FuncDeclType.Index() - extractFields(tw, decl.Recv, lbl, -1, -1) - extractExpr(tw, decl.Name, lbl, 0) - extractExpr(tw, decl.Type, lbl, 1) - extractStmt(tw, decl.Body, lbl, 2) - extractDoc(tw, decl.Doc, lbl) - extractTypeParamDecls(tw, decl.Type.TypeParams, lbl) - - // Note that we currently don't extract any kind of declaration for - // receiver type parameters. There isn't an explicit declaration, but - // we could consider the index/indices of an IndexExpr/IndexListExpr - // receiver as declarations. - default: - log.Fatalf("unknown declaration of type %T", decl) - } - dbscheme.DeclsTable.Emit(tw, lbl, kind, parent, idx) - extractNodeLocation(tw, decl, lbl) -} - -// extractSpec extracts AST information for the given declaration specifier -func extractSpec(tw *trap.Writer, spec ast.Spec, parent trap.Label, idx int) { - lbl := tw.Labeler.LocalID(spec) - var kind int - switch spec := spec.(type) { - case *ast.ImportSpec: - if spec == nil { - return - } - kind = dbscheme.ImportSpecType.Index() - extractExpr(tw, spec.Name, lbl, 0) - extractExpr(tw, spec.Path, lbl, 1) - extractDoc(tw, spec.Doc, lbl) - case *ast.ValueSpec: - if spec == nil { - return - } - kind = dbscheme.ValueSpecType.Index() - for i, name := range spec.Names { - extractExpr(tw, name, lbl, -(1 + i)) - } - extractExpr(tw, spec.Type, lbl, 0) - extractExprs(tw, spec.Values, lbl, 1, 1) - extractDoc(tw, spec.Doc, lbl) - case *ast.TypeSpec: - if spec == nil { - return - } - if spec.Assign.IsValid() { - kind = dbscheme.AliasSpecType.Index() - } else { - kind = dbscheme.TypeDefSpecType.Index() - } - extractExpr(tw, spec.Name, lbl, 0) - extractTypeParamDecls(tw, spec.TypeParams, lbl) - extractExpr(tw, spec.Type, lbl, 1) - extractDoc(tw, spec.Doc, lbl) - } - dbscheme.SpecsTable.Emit(tw, lbl, kind, parent, idx) - extractNodeLocation(tw, spec, lbl) -} - -// extractType extracts type information for `tp` and returns its associated label; -// types are only extracted once, so the second time `extractType` is invoked it simply returns the label -func extractType(tw *trap.Writer, tp types.Type) trap.Label { - lbl, exists := getTypeLabel(tw, tp) - if !exists { - var kind int - switch tp := tp.(type) { - case *types.Basic: - branch := dbscheme.BasicTypes[tp.Kind()] - if branch == nil { - log.Fatalf("unknown basic type %v", tp.Kind()) - } - kind = branch.Index() - case *types.Array: - kind = dbscheme.ArrayType.Index() - dbscheme.ArrayLengthTable.Emit(tw, lbl, fmt.Sprintf("%d", tp.Len())) - extractElementType(tw, lbl, tp.Elem()) - case *types.Slice: - kind = dbscheme.SliceType.Index() - extractElementType(tw, lbl, tp.Elem()) - case *types.Struct: - kind = dbscheme.StructType.Index() - for i := 0; i < tp.NumFields(); i++ { - field := tp.Field(i) - - // ensure the field is associated with a label - note that - // struct fields do not have a parent scope, so they are not - // dealt with by `extractScopes` - fieldlbl, exists := tw.Labeler.FieldID(field, i, lbl) - if !exists { - extractObject(tw, field, fieldlbl) - } - - dbscheme.FieldStructsTable.Emit(tw, fieldlbl, lbl) - - name := field.Name() - if field.Embedded() { - name = "" - } - extractComponentType(tw, lbl, i, name, field.Type()) - } - case *types.Pointer: - kind = dbscheme.PointerType.Index() - extractBaseType(tw, lbl, tp.Elem()) - case *types.Interface: - kind = dbscheme.InterfaceType.Index() - for i := 0; i < tp.NumMethods(); i++ { - meth := tp.Method(i) - - // Note that methods do not have a parent scope, so they are - // not dealt with by `extractScopes` - extractMethod(tw, meth) - - extractComponentType(tw, lbl, i, meth.Name(), meth.Type()) - } - for i := 0; i < tp.NumEmbeddeds(); i++ { - component := tp.EmbeddedType(i) - if isNonUnionTypeSetLiteral(component) { - component = createUnionFromType(component) - } - extractComponentType(tw, lbl, -(i + 1), "", component) - } - case *types.Tuple: - kind = dbscheme.TupleType.Index() - for i := 0; i < tp.Len(); i++ { - extractComponentType(tw, lbl, i, "", tp.At(i).Type()) - } - case *types.Signature: - kind = dbscheme.SignatureType.Index() - params, results := tp.Params(), tp.Results() - if params != nil { - for i := 0; i < params.Len(); i++ { - param := params.At(i) - extractComponentType(tw, lbl, i+1, "", param.Type()) - } - } - if results != nil { - for i := 0; i < results.Len(); i++ { - result := results.At(i) - extractComponentType(tw, lbl, -(i + 1), "", result.Type()) - } - } - if tp.Variadic() { - dbscheme.VariadicTable.Emit(tw, lbl) - } - case *types.Map: - kind = dbscheme.MapType.Index() - extractKeyType(tw, lbl, tp.Key()) - extractElementType(tw, lbl, tp.Elem()) - case *types.Chan: - kind = dbscheme.ChanTypes[tp.Dir()].Index() - extractElementType(tw, lbl, tp.Elem()) - case *types.Named: - origintp := tp.Origin() - kind = dbscheme.NamedType.Index() - dbscheme.TypeNameTable.Emit(tw, lbl, origintp.Obj().Name()) - underlying := origintp.Underlying() - extractUnderlyingType(tw, lbl, underlying) - trackInstantiatedStructFields(tw, tp, origintp) - - entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) - if entitylbl == trap.InvalidLabel { - log.Printf("Omitting type-object binding for unknown object %v.\n", origintp.Obj()) - } else { - if !exists { - extractObject(tw, origintp.Obj(), entitylbl) - } - dbscheme.TypeObjectTable.Emit(tw, lbl, entitylbl) - } - - // ensure all methods have labels - note that methods do not have a - // parent scope, so they are not dealt with by `extractScopes` - for i := 0; i < origintp.NumMethods(); i++ { - meth := origintp.Method(i) - - extractMethod(tw, meth) - } - - // associate all methods of underlying interface with this type - if underlyingInterface, ok := underlying.(*types.Interface); ok { - for i := 0; i < underlyingInterface.NumMethods(); i++ { - methlbl := extractMethod(tw, underlyingInterface.Method(i)) - dbscheme.MethodHostsTable.Emit(tw, methlbl, lbl) - } - } - case *types.TypeParam: - kind = dbscheme.TypeParamType.Index() - parentlbl := getTypeParamParentLabel(tw, tp) - constraintLabel := extractType(tw, tp.Constraint()) - dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index()) - case *types.Union: - kind = dbscheme.TypeSetLiteral.Index() - for i := 0; i < tp.Len(); i++ { - term := tp.Term(i) - tildeStr := "" - if term.Tilde() { - tildeStr = "~" - } - extractComponentType(tw, lbl, i, tildeStr, term.Type()) - } - default: - log.Fatalf("unexpected type %T", tp) - } - dbscheme.TypesTable.Emit(tw, lbl, kind) - } - return lbl -} - -// getTypeLabel looks up the label associated with `tp`, creating a new label if -// it does not have one yet; the second result indicates whether the label -// already existed -// -// Type labels refer to global keys to ensure that if the same type is -// encountered during the extraction of different files it is still ultimately -// mapped to the same entity. In particular, this means that keys for compound -// types refer to the labels of their component types. For named types, the key -// is constructed from their globally unique ID. This prevents cyclic type keys -// since type recursion in Go always goes through named types. -func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { - lbl, exists := tw.Labeler.TypeLabels[tp] - if !exists { - switch tp := tp.(type) { - case *types.Basic: - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%d;basictype", tp.Kind())) - case *types.Array: - len := tp.Len() - elem := extractType(tw, tp.Elem()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%d,{%s};arraytype", len, elem)) - case *types.Slice: - elem := extractType(tw, tp.Elem()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};slicetype", elem)) - case *types.Struct: - var b strings.Builder - for i := 0; i < tp.NumFields(); i++ { - field := tp.Field(i) - fieldTypeLbl := extractType(tw, field.Type()) - if i > 0 { - b.WriteString(",") - } - name := field.Name() - if field.Embedded() { - name = "" - } - fmt.Fprintf(&b, "%s,{%s},%s", name, fieldTypeLbl, util.EscapeTrapSpecialChars(tp.Tag(i))) - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;structtype", b.String())) - case *types.Pointer: - base := extractType(tw, tp.Elem()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};pointertype", base)) - case *types.Interface: - var b strings.Builder - for i := 0; i < tp.NumMethods(); i++ { - meth := tp.Method(i) - methLbl := extractType(tw, meth.Type()) - if i > 0 { - b.WriteString(",") - } - fmt.Fprintf(&b, "%s,{%s}", meth.Id(), methLbl) - } - b.WriteString(";") - for i := 0; i < tp.NumEmbeddeds(); i++ { - if i > 0 { - b.WriteString(",") - } - fmt.Fprintf(&b, "{%s}", extractType(tw, tp.EmbeddedType(i))) - } - // We note whether the interface is comparable so that we can - // distinguish the underlying type of `comparable` from an - // empty interface. - if tp.IsComparable() { - b.WriteString(";comparable") - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;interfacetype", b.String())) - case *types.Tuple: - var b strings.Builder - for i := 0; i < tp.Len(); i++ { - compLbl := extractType(tw, tp.At(i).Type()) - if i > 0 { - b.WriteString(",") - } - fmt.Fprintf(&b, "{%s}", compLbl) - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;tupletype", b.String())) - case *types.Signature: - var b strings.Builder - params, results := tp.Params(), tp.Results() - if params != nil { - for i := 0; i < params.Len(); i++ { - paramLbl := extractType(tw, params.At(i).Type()) - if i > 0 { - b.WriteString(",") - } - fmt.Fprintf(&b, "{%s}", paramLbl) - } - } - b.WriteString(";") - if results != nil { - for i := 0; i < results.Len(); i++ { - resultLbl := extractType(tw, results.At(i).Type()) - if i > 0 { - b.WriteString(",") - } - fmt.Fprintf(&b, "{%s}", resultLbl) - } - } - if tp.Variadic() { - b.WriteString(";variadic") - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;signaturetype", b.String())) - case *types.Map: - key := extractType(tw, tp.Key()) - value := extractType(tw, tp.Elem()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s},{%s};maptype", key, value)) - case *types.Chan: - dir := tp.Dir() - elem := extractType(tw, tp.Elem()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%v,{%s};chantype", dir, elem)) - case *types.Named: - origintp := tp.Origin() - entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) - if entitylbl == trap.InvalidLabel { - panic(fmt.Sprintf("Cannot construct label for named type %v (underlying object is %v).\n", origintp, origintp.Obj())) - } - if !exists { - extractObject(tw, origintp.Obj(), entitylbl) - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) - case *types.TypeParam: - parentlbl := getTypeParamParentLabel(tw, tp) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%s;typeparamtype", parentlbl, tp.Obj().Name())) - case *types.Union: - var b strings.Builder - for i := 0; i < tp.Len(); i++ { - compLbl := extractType(tw, tp.Term(i).Type()) - if i > 0 { - b.WriteString("|") - } - if tp.Term(i).Tilde() { - b.WriteString("~") - } - fmt.Fprintf(&b, "{%s}", compLbl) - } - lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;typesetliteraltype", b.String())) - default: - log.Fatalf("(getTypeLabel) unexpected type %T", tp) - } - tw.Labeler.TypeLabels[tp] = lbl - } - return lbl, exists -} - -// extractKeyType extracts `key` as the key type of the map type `mp` -func extractKeyType(tw *trap.Writer, mp trap.Label, key types.Type) { - dbscheme.KeyTypeTable.Emit(tw, mp, extractType(tw, key)) -} - -// extractElementType extracts `element` as the element type of the container type `container` -func extractElementType(tw *trap.Writer, container trap.Label, element types.Type) { - dbscheme.ElementTypeTable.Emit(tw, container, extractType(tw, element)) -} - -// extractBaseType extracts `base` as the base type of the pointer type `ptr` -func extractBaseType(tw *trap.Writer, ptr trap.Label, base types.Type) { - dbscheme.BaseTypeTable.Emit(tw, ptr, extractType(tw, base)) -} - -// extractUnderlyingType extracts `underlying` as the underlying type of the -// named type `named` -func extractUnderlyingType(tw *trap.Writer, named trap.Label, underlying types.Type) { - dbscheme.UnderlyingTypeTable.Emit(tw, named, extractType(tw, underlying)) -} - -// extractComponentType extracts `component` as the `idx`th component type of `parent` with name `name` -func extractComponentType(tw *trap.Writer, parent trap.Label, idx int, name string, component types.Type) { - dbscheme.ComponentTypesTable.Emit(tw, parent, idx, name, extractType(tw, component)) -} - -// extractNumLines extracts lines-of-code and lines-of-comments information for the -// given file -func extractNumLines(tw *trap.Writer, fileName string, ast *ast.File) { - f := tw.Package.Fset.File(ast.Pos()) - - lineCount := f.LineCount() - - // count lines of code by tokenizing - linesOfCode := 0 - src, err := ioutil.ReadFile(fileName) - if err != nil { - log.Fatalf("Unable to read file %s.", fileName) - } - var s scanner.Scanner - lastCodeLine := -1 - s.Init(f, src, nil, 0) - for { - pos, tok, lit := s.Scan() - if tok == token.EOF { - break - } else if tok != token.ILLEGAL && !(tok == token.SEMICOLON && lit == "\n") { - // specifically exclude newlines that are treated as semicolons - tkStartLine := f.Position(pos).Line - tkEndLine := tkStartLine + strings.Count(lit, "\n") - if tkEndLine > lastCodeLine { - if tkStartLine <= lastCodeLine { - // if the start line is the same as the last code line we've seen we don't want to double - // count it - // note tkStartLine < lastCodeLine should not be possible - linesOfCode += tkEndLine - lastCodeLine - } else { - linesOfCode += tkEndLine - tkStartLine + 1 - } - lastCodeLine = tkEndLine - } - } - } - - // count lines of comments by iterating over ast.Comments - linesOfComments := 0 - for _, cg := range ast.Comments { - for _, g := range cg.List { - fset := tw.Package.Fset - startPos, endPos := fset.Position(g.Pos()), fset.Position(g.End()) - linesOfComments += endPos.Line - startPos.Line + 1 - } - } - - dbscheme.NumlinesTable.Emit(tw, tw.Labeler.FileLabel(), lineCount, linesOfCode, linesOfComments) -} - -// For a type `t` which is the type of a field of an interface type, return -// whether `t` a type set literal which is not a union type. Note that a field -// of an interface must be a method signature, an embedded interface type or a -// type set literal. -func isNonUnionTypeSetLiteral(t types.Type) bool { - if t == nil { - return false - } - switch t.Underlying().(type) { - case *types.Interface, *types.Union, *types.Signature: - return false - default: - return true - } -} - -// Given a type `t`, return a union with a single term that is `t` without a -// tilde. -func createUnionFromType(t types.Type) *types.Union { - return types.NewUnion([]*types.Term{types.NewTerm(false, t)}) -} - -// Go through a `FieldList` and update the types of all type set literals which -// are not already union types to be union types. We do this by changing the -// types stored in `tw.Package.TypesInfo.Types`. Type set literals can only -// occur in two places: a type parameter declaration or a type in an interface. -func makeTypeSetLiteralsUnionTyped(tw *trap.Writer, fields *ast.FieldList) { - if fields == nil || fields.List == nil { - return - } - for i := 0; i < len(fields.List); i++ { - x := fields.List[i].Type - if _, alreadyOverridden := tw.TypesOverride[x]; !alreadyOverridden { - xtp := typeOf(tw, x) - if isNonUnionTypeSetLiteral(xtp) { - tw.TypesOverride[x] = createUnionFromType(xtp) - } - } - } -} - -func typeOf(tw *trap.Writer, e ast.Expr) types.Type { - if val, ok := tw.TypesOverride[e]; ok { - return val - } - return tw.Package.TypesInfo.TypeOf(e) -} - -func flattenBinaryExprTree(tw *trap.Writer, e ast.Expr, parent trap.Label, idx int) int { - binaryexpr, ok := e.(*ast.BinaryExpr) - if ok { - idx = flattenBinaryExprTree(tw, binaryexpr.X, parent, idx) - idx = flattenBinaryExprTree(tw, binaryexpr.Y, parent, idx) - } else { - extractExpr(tw, e, parent, idx) - idx = idx + 1 - } - return idx -} - -func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.Label) { - if fields == nil || fields.List == nil { - return - } - - // Type set literals can occur as the type in a type parameter declaration, - // so we ensure that they are union typed. - makeTypeSetLiteralsUnionTyped(tw, fields) - - idx := 0 - for _, field := range fields.List { - lbl := tw.Labeler.LocalID(field) - dbscheme.TypeParamDeclsTable.Emit(tw, lbl, parent, idx) - extractNodeLocation(tw, field, lbl) - if field.Names != nil { - for i, name := range field.Names { - extractExpr(tw, name, lbl, i+1) - } - } - extractExpr(tw, field.Type, lbl, 0) - extractDoc(tw, field.Doc, lbl) - idx += 1 - } -} - -// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams` -func populateTypeParamParents(tw *trap.Writer, typeparams *types.TypeParamList, parent types.Object) { - if typeparams != nil { - for idx := 0; idx < typeparams.Len(); idx++ { - setTypeParamParent(typeparams.At(idx), parent) - } - } -} - -// getobjectBeingUsed looks up `ident` in `tw.Package.TypesInfo.Uses` and makes -// some changes to the object to avoid returning objects relating to instantiated -// types. -func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { - obj := tw.Package.TypesInfo.Uses[ident] - if obj == nil { - return nil - } - if override, ok := tw.ObjectsOverride[obj]; ok { - return override - } - if funcObj, ok := obj.(*types.Func); ok { - sig := funcObj.Type().(*types.Signature) - if recv := sig.Recv(); recv != nil { - recvType := recv.Type() - originType, isSame := tryGetGenericType(recvType) - - if originType == nil { - if pointerType, ok := recvType.(*types.Pointer); ok { - originType, isSame = tryGetGenericType(pointerType.Elem()) - } - } - - if originType == nil || isSame { - return obj - } - - for i := 0; i < originType.NumMethods(); i++ { - meth := originType.Method(i) - if meth.Name() == funcObj.Name() { - return meth - } - } - if interfaceType, ok := originType.Underlying().(*types.Interface); ok { - for i := 0; i < interfaceType.NumMethods(); i++ { - meth := interfaceType.Method(i) - if meth.Name() == funcObj.Name() { - return meth - } - } - } - log.Fatalf("Could not find method %s on type %s", funcObj.Name(), originType) - } - } - - return obj -} - -// tryGetGenericType returns the generic type of `tp`, and a boolean indicating -// whether it is the same as `tp`. -func tryGetGenericType(tp types.Type) (*types.Named, bool) { - if namedType, ok := tp.(*types.Named); ok { - originType := namedType.Origin() - return originType, namedType == originType - } - return nil, false -} - -// trackInstantiatedStructFields tries to give the fields of an instantiated -// struct type underlying `tp` the same labels as the corresponding fields of -// the generic struct type. This is so that when we come across the -// instantiated field in `tw.Package.TypesInfo.Uses` we will get the label for -// the generic field instead. -func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) { - if tp == origintp { - return - } - - if instantiatedStruct, ok := tp.Underlying().(*types.Struct); ok { - genericStruct, ok2 := origintp.Underlying().(*types.Struct) - if !ok2 { - log.Fatalf( - "Error: underlying type of instantiated type is a struct but underlying type of generic type is %s", - origintp.Underlying()) - } - - if instantiatedStruct.NumFields() != genericStruct.NumFields() { - log.Fatalf( - "Error: instantiated struct %s has different number of fields than the generic version %s (%d != %d)", - instantiatedStruct, genericStruct, instantiatedStruct.NumFields(), genericStruct.NumFields()) - } - - for i := 0; i < instantiatedStruct.NumFields(); i++ { - tw.ObjectsOverride[instantiatedStruct.Field(i)] = genericStruct.Field(i) - } - } -} - -func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label { - parent, exists := typeParamParent[tp] - if !exists { - log.Fatalf("Parent of type parameter does not exist: %s %s", tp.String(), tp.Constraint().String()) - } - parentlbl, _ := tw.Labeler.ScopedObjectID(parent, func() trap.Label { - log.Fatalf("getTypeLabel() called for parent of type parameter %s", tp.String()) - return trap.InvalidLabel - }) - return parentlbl -} - -func setTypeParamParent(tp *types.TypeParam, newobj types.Object) { - obj, exists := typeParamParent[tp] - if !exists { - typeParamParent[tp] = newobj - } else if newobj != obj { - log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), obj, newobj) - } -} diff --git a/extractor/gomodextractor.go b/extractor/gomodextractor.go deleted file mode 100644 index 5a6a26281..000000000 --- a/extractor/gomodextractor.go +++ /dev/null @@ -1,201 +0,0 @@ -package extractor - -import ( - "fmt" - "golang.org/x/mod/modfile" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" - - "github.com/github/codeql-go/extractor/dbscheme" - "github.com/github/codeql-go/extractor/srcarchive" - "github.com/github/codeql-go/extractor/trap" -) - -func (extraction *Extraction) extractGoMod(path string) error { - if normPath, err := filepath.EvalSymlinks(path); err == nil { - path = normPath - } - - extraction.Lock.Lock() - if extraction.SeenGoMods[path] { - extraction.Lock.Unlock() - return nil - } - - extraction.SeenGoMods[path] = true - extraction.Lock.Unlock() - - tw, err := trap.NewWriter(path, nil) - if err != nil { - return err - } - defer tw.Close() - - err = srcarchive.Add(path) - if err != nil { - return err - } - - extraction.extractFileInfo(tw, path) - - file, err := os.Open(path) - if err != nil { - return fmt.Errorf("failed to open go.mod file %s: %s", path, err.Error()) - } - data, err := ioutil.ReadAll(file) - if err != nil { - return fmt.Errorf("failed to read go.mod file %s: %s", path, err.Error()) - } - - modfile, err := modfile.Parse(path, data, nil) - if err != nil { - return fmt.Errorf("failed to parse go.mod file %s: %s", path, err.Error()) - } - - extractGoModFile(tw, modfile.Syntax) - - return nil -} - -type commentGroupIdxAllocator struct { - nextIdx int -} - -func (cgIdxAlloc *commentGroupIdxAllocator) nextCgIdx() int { - ret := cgIdxAlloc.nextIdx - cgIdxAlloc.nextIdx++ - return ret -} - -func extractGoModFile(tw *trap.Writer, file *modfile.FileSyntax) { - cgIdxAlloc := commentGroupIdxAllocator{0} - - for idx, stmt := range file.Stmt { - extractGoModExpr(tw, stmt, tw.Labeler.FileLabel(), idx, &cgIdxAlloc) - } - - extractGoModComments(tw, file, tw.Labeler.FileLabel(), &cgIdxAlloc) -} - -func extractGoModExpr(tw *trap.Writer, expr modfile.Expr, parent trap.Label, idx int, cgIdxAlloc *commentGroupIdxAllocator) { - lbl := tw.Labeler.LocalID(expr) - - var kind int - switch expr := expr.(type) { - case *modfile.CommentBlock: - kind = dbscheme.ModCommentBlockType.Index() - case *modfile.LParen: - kind = dbscheme.ModLParenType.Index() - case *modfile.RParen: - kind = dbscheme.ModRParenType.Index() - case *modfile.Line: - kind = dbscheme.ModLineType.Index() - for idx, tok := range expr.Token { - dbscheme.ModTokensTable.Emit(tw, tok, lbl, idx) - } - case *modfile.LineBlock: - kind = dbscheme.ModLineBlockType.Index() - for idx, tok := range expr.Token { - dbscheme.ModTokensTable.Emit(tw, tok, lbl, idx) - } - extractGoModExpr(tw, &expr.LParen, lbl, 0, cgIdxAlloc) - for idx, line := range expr.Line { - extractGoModExpr(tw, line, lbl, idx+1, cgIdxAlloc) - } - extractGoModExpr(tw, &expr.RParen, lbl, len(expr.Line)+1, cgIdxAlloc) - default: - log.Fatalf("unknown go.mod expression of type %T", expr) - } - - dbscheme.ModExprsTable.Emit(tw, lbl, kind, parent, idx) - - extractGoModComments(tw, expr, lbl, cgIdxAlloc) - - start, end := expr.Span() - extractLocation(tw, lbl, start.Line, start.LineRune, end.Line, end.LineRune) -} - -type GoModExprCommentWrapper struct { - expr modfile.Expr -} - -func minInt(a int, b int) int { - if a < b { - return a - } - return b -} - -func maxInt(a int, b int) int { - if a > b { - return a - } - return b -} - -func lexMin(a1 int, a2 int, b1 int, b2 int) (int, int) { - if a1 < b1 { - return a1, a2 - } else if a1 > b1 { - return b1, b2 - } else { - return a1, minInt(b1, b2) - } -} - -func lexMax(a1 int, a2 int, b1 int, b2 int) (int, int) { - if a1 < b1 { - return b1, b2 - } else if a1 > b1 { - return a1, a2 - } else { - return a1, maxInt(b1, b2) - } -} - -func extractGoModComments(tw *trap.Writer, expr modfile.Expr, exprlbl trap.Label, cgIdxAlloc *commentGroupIdxAllocator) { - comments := expr.Comment() - - if len(comments.Before) == 0 && len(comments.Suffix) == 0 && len(comments.After) == 0 { - return - } - - // extract a pseudo `@commentgroup` for each expr that contains their associated comments - grouplbl := tw.Labeler.LocalID(GoModExprCommentWrapper{expr}) - dbscheme.CommentGroupsTable.Emit(tw, grouplbl, tw.Labeler.FileLabel(), cgIdxAlloc.nextCgIdx()) - dbscheme.DocCommentsTable.Emit(tw, exprlbl, grouplbl) - - var allComments []modfile.Comment - allComments = append(allComments, comments.Before...) - allComments = append(allComments, comments.Suffix...) - allComments = append(allComments, comments.After...) - - var startLine, startCol, endLine, endCol int = 0, 0, 0, 0 - var first bool = true - idx := 0 - for _, comment := range allComments { - commentToken := strings.TrimSuffix(strings.TrimSuffix(comment.Token, "\n"), "\r") - extractGoModComment(tw, comment, commentToken, grouplbl, idx) - idx++ - commentEndCol := comment.Start.LineRune + (len(commentToken) - 1) - if first { - startLine, startCol, endLine, endCol = comment.Start.Line, comment.Start.LineRune, comment.Start.Line, commentEndCol - first = false - } else { - startLine, startCol = lexMin(comment.Start.Line, comment.Start.LineRune, startLine, startCol) - endLine, endCol = lexMax(comment.Start.Line, commentEndCol, endLine, endCol) - } - } - - extractLocation(tw, grouplbl, startLine, startCol, endLine, endCol) -} - -func extractGoModComment(tw *trap.Writer, comment modfile.Comment, commentToken string, grouplbl trap.Label, idx int) { - lbl := tw.Labeler.LocalID(comment) - dbscheme.CommentsTable.Emit(tw, lbl, dbscheme.SlashSlashComment.Index(), grouplbl, idx, commentToken) - - extractLocation(tw, lbl, comment.Start.Line, comment.Start.LineRune, comment.Start.Line, comment.Start.LineRune+(len(commentToken)-1)) -} diff --git a/extractor/net/sourceforge/pmd/cpd/AbstractLanguage.java b/extractor/net/sourceforge/pmd/cpd/AbstractLanguage.java deleted file mode 100644 index 363be4e8f..000000000 --- a/extractor/net/sourceforge/pmd/cpd/AbstractLanguage.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.sourceforge.pmd.cpd; - -/* - * This is a stub definition for pmd's AbstractLanguage class - * including only the API used by the GoLanguage class. - */ - -public abstract class AbstractLanguage { - - public AbstractLanguage(String... extensions) {} - - public abstract Tokenizer getTokenizer(boolean fuzzyMatch); -} diff --git a/extractor/net/sourceforge/pmd/cpd/GoLanguage.java b/extractor/net/sourceforge/pmd/cpd/GoLanguage.java deleted file mode 100644 index 400a52c0e..000000000 --- a/extractor/net/sourceforge/pmd/cpd/GoLanguage.java +++ /dev/null @@ -1,68 +0,0 @@ -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.ProcessBuilder.Redirect; -import java.nio.charset.Charset; -import java.nio.file.Paths; -import java.util.List; -import opencsv.CSVReader; - -public class GoLanguage extends AbstractLanguage { - public GoLanguage() { - super(".go"); - } - - @Override - public Tokenizer getTokenizer(final boolean fuzzyMatch) { - return new Tokenizer() { - @Override - public void tokenize(SourceCode tokens, List tokenEntries) { - String fileName = tokens.getFileName(); - String platform = "linux", exe = ""; - - String osName = System.getProperty("os.name", "unknown"); - if (osName.contains("Windows")) { - platform = "win"; - exe = ".exe"; - } else if (osName.contains("Mac OS X")) { - platform = "osx"; - } - - // get tools folder from SEMMLE_DIST - String toolsDir = null; - String dist = System.getenv("SEMMLE_DIST"); - if (dist != null && !dist.isEmpty()) { - toolsDir = dist + "/language-packs/go/tools/platform/" + platform; - } - - String goTokenizer = toolsDir == null ? "go-tokenizer" : toolsDir + "/bin/go-tokenizer"; - goTokenizer += exe; - ProcessBuilder pb = new ProcessBuilder(Paths.get(goTokenizer).toString(), fileName); - pb.redirectError(Redirect.INHERIT); - try { - Process process = pb.start(); - try ( - CSVReader r = new CSVReader(new InputStreamReader(process.getInputStream(), Charset.forName("UTF-8"))) - ) { - String[] row; - while ((row = r.readNext()) != null) { - String text = row[0]; - String fuzzyText = row[1]; - int beginLine = Integer.parseInt(row[2]); - int beginColumn = Integer.parseInt(row[3]); - int endLine = Integer.parseInt(row[4]); - int endColumn = Integer.parseInt(row[5]); - tokenEntries.add(new TokenEntry(fuzzyMatch ? text : fuzzyText, fileName, beginLine, beginColumn, endLine, endColumn)); - } - } - int exitCode = process.waitFor(); - if (exitCode != 0) - throw new RuntimeException("Tokenizing " + fileName + " returned " + exitCode + "."); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } - }; - } -} diff --git a/extractor/net/sourceforge/pmd/cpd/SourceCode.java b/extractor/net/sourceforge/pmd/cpd/SourceCode.java deleted file mode 100644 index f3ad49f83..000000000 --- a/extractor/net/sourceforge/pmd/cpd/SourceCode.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.sourceforge.pmd.cpd; - -/* - * This is a stub definition for pmd's SourceCode class - * including only the API used by the GoLanguage class. - */ - -public class SourceCode { - public String getFileName() { - return null; - } -} diff --git a/extractor/net/sourceforge/pmd/cpd/TokenEntry.java b/extractor/net/sourceforge/pmd/cpd/TokenEntry.java deleted file mode 100644 index d599ebea3..000000000 --- a/extractor/net/sourceforge/pmd/cpd/TokenEntry.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.sourceforge.pmd.cpd; - -/* - * This is a stub definition for pmd's TokenEntry class - * including only the API used by the GoLanguage class. - */ - -public class TokenEntry { - public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endLine, int endColumn) { - } -} diff --git a/extractor/net/sourceforge/pmd/cpd/Tokenizer.java b/extractor/net/sourceforge/pmd/cpd/Tokenizer.java deleted file mode 100644 index cc94bbbb1..000000000 --- a/extractor/net/sourceforge/pmd/cpd/Tokenizer.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.sourceforge.pmd.cpd; - -/* - * This is a stub definition for pmd's Tokenizer interface - * including only the API used by the GoLanguage class. - */ - -import java.util.List; - -public interface Tokenizer { - void tokenize(SourceCode tokens, List tokenEntries); -} diff --git a/extractor/opencsv/CSVParser.java b/extractor/opencsv/CSVParser.java deleted file mode 100644 index e3a864bef..000000000 --- a/extractor/opencsv/CSVParser.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - Copyright 2005 Bytecode Pty Ltd. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package opencsv; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * A very simple CSV parser released under a commercial-friendly license. - * This just implements splitting a single line into fields. - * - * @author Glen Smith - * @author Rainer Pruy - * - */ -public class CSVParser { - - private final char separator; - - private final char quotechar; - - private final char escape; - - private final boolean strictQuotes; - - private StringBuilder buf = new StringBuilder(INITIAL_READ_SIZE); - - /** The default separator to use if none is supplied to the constructor. */ - public static final char DEFAULT_SEPARATOR = ','; - - private static final int INITIAL_READ_SIZE = 128; - - /** - * The default quote character to use if none is supplied to the - * constructor. - */ - public static final char DEFAULT_QUOTE_CHARACTER = '"'; - - - /** - * The default escape character to use if none is supplied to the - * constructor. - */ - public static final char DEFAULT_ESCAPE_CHARACTER = '"'; - - /** - * The default strict quote behavior to use if none is supplied to the - * constructor - */ - public static final boolean DEFAULT_STRICT_QUOTES = false; - - /** - * Constructs CSVReader with supplied separator and quote char. - * Allows setting the "strict quotes" flag - * @param separator - * the delimiter to use for separating entries - * @param quotechar - * the character to use for quoted elements - * @param escape - * the character to use for escaping a separator or quote - * @param strictQuotes - * if true, characters outside the quotes are ignored - */ - CSVParser(char separator, char quotechar, char escape, boolean strictQuotes) { - this.separator = separator; - this.quotechar = quotechar; - this.escape = escape; - this.strictQuotes = strictQuotes; - } - - /** - * - * @return true if something was left over from last call(s) - */ - public boolean isPending() { - return buf.length() != 0; - } - - public String[] parseLineMulti(String nextLine) throws IOException { - return parseLine(nextLine, true); - } - - public String[] parseLine(String nextLine) throws IOException { - return parseLine(nextLine, false); - } - /** - * Parses an incoming String and returns an array of elements. - * - * @param nextLine - * the string to parse - * @return the comma-tokenized list of elements, or null if nextLine is null - * @throws IOException if bad things happen during the read - */ - private String[] parseLine(String nextLine, boolean multi) throws IOException { - - if (!multi && isPending()) { - clear(); - } - - if (nextLine == null) { - if (isPending()) { - String s = buf.toString(); - clear(); - return new String[] {s}; - } else { - return null; - } - } - - ListtokensOnThisLine = new ArrayList(); - boolean inQuotes = isPending(); - for (int i = 0; i < nextLine.length(); i++) { - - char c = nextLine.charAt(i); - if (c == this.escape && isNextCharacterEscapable(nextLine, inQuotes, i)) { - buf.append(nextLine.charAt(i+1)); - i++; - } else if (c == quotechar) { - if( isNextCharacterEscapedQuote(nextLine, inQuotes, i) ){ - buf.append(nextLine.charAt(i+1)); - i++; - }else{ - inQuotes = !inQuotes; - // the tricky case of an embedded quote in the middle: a,bc"d"ef,g - if (!strictQuotes) { - if(i>2 //not on the beginning of the line - && nextLine.charAt(i-1) != this.separator //not at the beginning of an escape sequence - && nextLine.length()>(i+1) && - nextLine.charAt(i+1) != this.separator //not at the end of an escape sequence - ){ - buf.append(c); - } - } - } - } else if (c == separator && !inQuotes) { - tokensOnThisLine.add(buf.toString()); - clear(); // start work on next token - } else { - if (!strictQuotes || inQuotes) - buf.append(c); - } - } - // line is done - check status - if (inQuotes) { - if (multi) { - // continuing a quoted section, re-append newline - buf.append('\n'); - // this partial content is not to be added to field list yet - } else { - throw new IOException("Un-terminated quoted field at end of CSV line"); - } - } else { - tokensOnThisLine.add(buf.toString()); - clear(); - } - return tokensOnThisLine.toArray(new String[tokensOnThisLine.size()]); - - } - - /** - * precondition: the current character is a quote or an escape - * @param nextLine the current line - * @param inQuotes true if the current context is quoted - * @param i current index in line - * @return true if the following character is a quote - */ - private boolean isNextCharacterEscapedQuote(String nextLine, boolean inQuotes, int i) { - return inQuotes // we are in quotes, therefore there can be escaped quotes in here. - && nextLine.length() > (i+1) // there is indeed another character to check. - && nextLine.charAt(i+1) == quotechar; - } - - /** - * precondition: the current character is an escape - * @param nextLine the current line - * @param inQuotes true if the current context is quoted - * @param i current index in line - * @return true if the following character is a quote - */ - protected boolean isNextCharacterEscapable(String nextLine, boolean inQuotes, int i) { - return inQuotes // we are in quotes, therefore there can be escaped quotes in here. - && nextLine.length() > (i+1) // there is indeed another character to check. - && ( nextLine.charAt(i+1) == quotechar || nextLine.charAt(i+1) == this.escape); - } - - /** - * Reset the buffer used for storing the current field's value - */ - private void clear() { - buf.setLength(0); - } -} diff --git a/extractor/opencsv/CSVReader.java b/extractor/opencsv/CSVReader.java deleted file mode 100644 index 11b537e49..000000000 --- a/extractor/opencsv/CSVReader.java +++ /dev/null @@ -1,192 +0,0 @@ -/** - Copyright 2005 Bytecode Pty Ltd. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package opencsv; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; - -/** - * A very simple CSV reader released under a commercial-friendly license. - * - * @author Glen Smith - * - */ -public class CSVReader implements Closeable { - - private final BufferedReader br; - - private boolean hasNext = true; - - private final CSVParser parser; - - private final int skipLines; - - private boolean linesSkipped; - - /** The line number of the last physical line read (one-based). */ - private int curline = 0; - - /** The physical line number at which the last logical line read started (one-based). */ - private int startLine = 0; - - /** - * The default line to start reading. - */ - private static final int DEFAULT_SKIP_LINES = 0; - - /** - * Constructs CSVReader using a comma for the separator. - * - * @param reader - * the reader to an underlying CSV source. - */ - public CSVReader(Reader reader) { - this(reader, - CSVParser.DEFAULT_SEPARATOR, CSVParser.DEFAULT_QUOTE_CHARACTER, - CSVParser.DEFAULT_ESCAPE_CHARACTER, DEFAULT_SKIP_LINES, - CSVParser.DEFAULT_STRICT_QUOTES); - } - - /** - * Constructs CSVReader with supplied separator and quote char. - * - * @param reader - * the reader to an underlying CSV source. - * @param separator - * the delimiter to use for separating entries - * @param quotechar - * the character to use for quoted elements - * @param escape - * the character to use for escaping a separator or quote - * @param line - * the line number to skip for start reading - * @param strictQuotes - * sets if characters outside the quotes are ignored - */ - private CSVReader(Reader reader, char separator, char quotechar, char escape, int line, boolean strictQuotes) { - this.br = new BufferedReader(reader); - this.parser = new CSVParser(separator, quotechar, escape, strictQuotes); - this.skipLines = line; - } - - - /** - * Reads the entire file into a List with each element being a String[] of - * tokens. - * - * @return a List of String[], with each String[] representing a line of the - * file. - * - * @throws IOException - * if bad things happen during the read - */ - public List readAll() throws IOException { - - List allElements = new ArrayList(); - while (hasNext) { - String[] nextLineAsTokens = readNext(); - if (nextLineAsTokens != null) - allElements.add(nextLineAsTokens); - } - return allElements; - - } - - /** - * Reads the next line from the buffer and converts to a string array. - * - * @return a string array with each comma-separated element as a separate - * entry, or null if there are no more lines to read. - * - * @throws IOException - * if bad things happen during the read - */ - public String[] readNext() throws IOException { - boolean first = true; - String[] result = null; - do { - String nextLine = getNextLine(); - - if (first) { - startLine = curline; - first = false; - } - - if (!hasNext) { - return result; // should throw if still pending? - } - String[] r = parser.parseLineMulti(nextLine); - if (r.length > 0) { - if (result == null) { - result = r; - } else { - String[] t = new String[result.length+r.length]; - System.arraycopy(result, 0, t, 0, result.length); - System.arraycopy(r, 0, t, result.length, r.length); - result = t; - } - } - } while (parser.isPending()); - return result; - } - - /** - * Reads the next line from the file. - * - * @return the next line from the file without trailing newline - * @throws IOException - * if bad things happen during the read - */ - private String getNextLine() throws IOException { - if (!this.linesSkipped) { - for (int i = 0; i < skipLines; i++) { - br.readLine(); - ++curline; - } - this.linesSkipped = true; - } - String nextLine = br.readLine(); - if (nextLine == null) { - hasNext = false; - } else { - ++curline; - } - return hasNext ? nextLine : null; - } - - /** - * Closes the underlying reader. - * - * @throws IOException if the close fails - */ - @Override - public void close() throws IOException{ - br.close(); - } - - /** - * Return the physical line number (one-based) at which the last logical line read started, - * or zero if no line has been read yet. - */ - public int getStartLine() { - return startLine; - } -} diff --git a/extractor/semaphore.go b/extractor/semaphore.go deleted file mode 100644 index 127f94aa6..000000000 --- a/extractor/semaphore.go +++ /dev/null @@ -1,42 +0,0 @@ -package extractor - -import ( - "log" -) - -type Unit struct{} - -var unit = Unit{} - -type semaphore struct { - counter, lock chan Unit -} - -func (s *semaphore) acquire(n int) { - if s != nil { - if cap(s.counter) < n { - log.Fatalf("Tried to acquire more resources than were available.") - } - s.lock <- unit - for i := 0; i < n; i++ { - s.counter <- unit - } - <-s.lock - } -} - -func (s *semaphore) release(n int) { - if s != nil { - for i := 0; i < n; i++ { - <-s.counter - } - } -} - -func newSemaphore(max int) *semaphore { - if max > 0 { - return &semaphore{make(chan Unit, max), make(chan Unit, 1)} - } else { - return nil - } -} diff --git a/extractor/srcarchive/projectlayout.go b/extractor/srcarchive/projectlayout.go deleted file mode 100644 index da717dd42..000000000 --- a/extractor/srcarchive/projectlayout.go +++ /dev/null @@ -1,105 +0,0 @@ -package srcarchive - -import ( - "bufio" - "errors" - "fmt" - "os" - "strings" -) - -// ProjectLayout describes a very simple project layout rewriting paths starting -// with `from` to start with `to` instead. -// -// We currently only support project layouts of the form -// -// # to -// from// -type ProjectLayout struct { - from, to string -} - -// normaliseSlashes adds an initial slash to `path` if there isn't one, and trims -// a final slash if there is one -func normaliseSlashes(path string) string { - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - return strings.TrimSuffix(path, "/") -} - -// LoadProjectLayout loads a project layout from the given file, returning an error -// if the file does not have the right format -func LoadProjectLayout(file *os.File) (*ProjectLayout, error) { - res := ProjectLayout{} - scanner := bufio.NewScanner(file) - - line := "" - for ; line == "" && scanner.Scan(); line = strings.TrimSpace(scanner.Text()) { - } - - if !strings.HasPrefix(line, "#") { - return nil, fmt.Errorf("first line of project layout should start with #, but got %s", line) - } - res.to = normaliseSlashes(strings.TrimSpace(strings.TrimPrefix(line, "#"))) - - if !scanner.Scan() { - return nil, errors.New("empty section in project-layout file") - } - - line = strings.TrimSpace(scanner.Text()) - - if !strings.HasSuffix(line, "//") { - return nil, errors.New("unsupported project-layout feature") - } - line = strings.TrimSuffix(line, "//") - - if strings.HasPrefix(line, "-") || strings.Contains(line, "*") || strings.Contains(line, "//") { - return nil, errors.New("unsupported project-layout feature") - } - res.from = normaliseSlashes(line) - - for scanner.Scan() { - if strings.TrimSpace(scanner.Text()) != "" { - return nil, errors.New("only one section with one rewrite supported") - } - } - - return &res, nil -} - -// transformString transforms `str` as specified by the project layout: if it starts with the `from` -// prefix, that prefix is relaced by `to`; otherwise the string is returned unchanged -func (p *ProjectLayout) transformString(str string) string { - if str == p.from { - return p.to - } - if strings.HasPrefix(str, p.from+"/") { - return p.to + "/" + str[len(p.from)+1:] - } - return str -} - -// isWindowsPath checks whether the substring of `path` starting at `idx` looks like a (slashified) -// Windows path, that is, starts with a drive letter followed by a colon and a slash -func isWindowsPath(path string, idx int) bool { - return len(path) >= 3+idx && - path[idx] != '/' && - path[idx+1] == ':' && path[idx+2] == '/' -} - -// Transform transforms the given path according to the project layout: if it starts with the `from` -// prefix, that prefix is relaced by `to`; otherwise the path is returned unchanged. -// -// Unlike the (internal) method `transformString`, this method handles Windows paths sensibly. -func (p *ProjectLayout) Transform(path string) string { - if isWindowsPath(path, 0) { - result := p.transformString("/" + path) - if isWindowsPath(result, 1) && result[0] == '/' { - return result[1:] - } - return result - } else { - return p.transformString(path) - } -} diff --git a/extractor/srcarchive/projectlayout_test.go b/extractor/srcarchive/projectlayout_test.go deleted file mode 100644 index fb9f180ff..000000000 --- a/extractor/srcarchive/projectlayout_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package srcarchive - -import ( - "io/ioutil" - "os" - "testing" -) - -func mkProjectLayout(projectLayoutSource string, t *testing.T) (*ProjectLayout, error) { - pt, err := ioutil.TempFile("", "path-transformer") - if err != nil { - t.Fatalf("Unable to create temporary file for project layout: %s", err.Error()) - } - defer os.Remove(pt.Name()) - _, err = pt.WriteString(projectLayoutSource) - if err != nil { - t.Fatalf("Unable to write to temporary file for project layout: %s", err.Error()) - } - err = pt.Close() - if err != nil { - t.Fatalf("Unable to close path transformer file: %s.", err.Error()) - } - - pt, err = os.Open(pt.Name()) - if err != nil { - t.Fatalf("Unable to open path transformer file: %s.", err.Error()) - } - - return LoadProjectLayout(pt) -} - -func testTransformation(projectLayout *ProjectLayout, t *testing.T, path string, expected string) { - actual := projectLayout.Transform(path) - if actual != expected { - t.Errorf("Expected %s to be transformed to %s, but got %s", path, expected, actual) - } -} - -func TestValidProjectLayout(t *testing.T) { - p, err := mkProjectLayout(` -# /opt/src -/opt/src/root/src/org/repo// -`, t) - - if err != nil { - t.Fatalf("Error loading project layout: %s", err.Error()) - } - - testTransformation(p, t, "/opt/src/root/src/org/repo", "/opt/src") - testTransformation(p, t, "/opt/src/root/src/org/repo/", "/opt/src/") - testTransformation(p, t, "/opt/src/root/src/org/repo/main.go", "/opt/src/main.go") - testTransformation(p, t, "/opt/not/in/src", "/opt/not/in/src") - testTransformation(p, t, "/opt/src/root/srcorg/repo", "/opt/src/root/srcorg/repo") - testTransformation(p, t, "opt/src/root/src/org/repo", "opt/src/root/src/org/repo") -} - -func TestWindowsPaths(t *testing.T) { - p, err := mkProjectLayout(` -# /c:/virtual -/d:// -`, t) - - if err != nil { - t.Fatalf("Error loading project layout: %s", err.Error()) - } - - testTransformation(p, t, "d:/foo", "c:/virtual/foo") -} - -func TestWindowsToUnixPaths(t *testing.T) { - p, err := mkProjectLayout(` -# /opt/src -/d:// -`, t) - - if err != nil { - t.Fatalf("Error loading project layout: %s", err.Error()) - } - - testTransformation(p, t, "d:/foo", "/opt/src/foo") -} - -func TestEmptyProjectLayout(t *testing.T) { - _, err := mkProjectLayout("", t) - if err == nil { - t.Error("Expected error on empty project layout") - } -} - -func TestEmptyProjectLayout2(t *testing.T) { - _, err := mkProjectLayout(` - `, t) - if err == nil { - t.Error("Expected error on empty project layout") - } -} - -func TestExclusion(t *testing.T) { - _, err := mkProjectLayout(` -# /opt/src --/foo// -`, t) - if err == nil { - t.Error("Expected error on exclusion") - } -} - -func TestStar(t *testing.T) { - _, err := mkProjectLayout(` -# /opt/src -/foo/**/bar// -`, t) - if err == nil { - t.Error("Expected error on star") - } -} - -func TestDoubleSlash(t *testing.T) { - _, err := mkProjectLayout(` -# /opt/src -/foo//bar// -`, t) - if err == nil { - t.Error("Expected error on multiple double slashes") - } -} - -func TestInternalDoubleSlash(t *testing.T) { - _, err := mkProjectLayout(` -# /opt/src -/foo//bar -`, t) - if err == nil { - t.Error("Expected error on internal double slash") - } -} diff --git a/extractor/srcarchive/srcarchive.go b/extractor/srcarchive/srcarchive.go deleted file mode 100644 index ee9aea430..000000000 --- a/extractor/srcarchive/srcarchive.go +++ /dev/null @@ -1,85 +0,0 @@ -package srcarchive - -import ( - "errors" - "io" - "log" - "os" - "path/filepath" - "strings" -) - -var pathTransformer *ProjectLayout - -func init() { - pt := os.Getenv("SEMMLE_PATH_TRANSFORMER") - if pt != "" { - ptf, err := os.Open(pt) - if err != nil { - log.Fatalf("Unable to open path transformer %s: %s.\n", pt, err.Error()) - } - pathTransformer, err = LoadProjectLayout(ptf) - if err != nil { - log.Fatalf("Unable to initialize path transformer: %s.\n", err.Error()) - } - } -} - -// Add inserts the file with the given `path` into the source archive, returning a non-nil -// error value if it fails -func Add(path string) error { - srcArchive, err := srcArchive() - if err != nil { - return err - } - - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - - archiveFilePath := filepath.Join(srcArchive, AppendablePath(path)) - err = os.MkdirAll(filepath.Dir(archiveFilePath), 0755) - if err != nil { - return err - } - archiveFile, err := os.Create(archiveFilePath) - if err != nil { - return err - } - defer archiveFile.Close() - - _, err = io.Copy(archiveFile, file) - return err -} - -func srcArchive() (string, error) { - srcArchive := os.Getenv("CODEQL_EXTRACTOR_GO_SOURCE_ARCHIVE_DIR") - if srcArchive == "" { - srcArchive = os.Getenv("SOURCE_ARCHIVE") - } - if srcArchive == "" { - return "", errors.New("environment variable CODEQL_EXTRACTOR_GO_SOURCE_ARCHIVE_DIR not set") - } - err := os.MkdirAll(srcArchive, 0755) - if err != nil { - return "", err - } - return srcArchive, nil -} - -// TransformPath applies the transformations specified by `SEMMLE_PATH_TRANSFORMER` (if any) to the -// given path -func TransformPath(path string) string { - if pathTransformer != nil { - return filepath.FromSlash(pathTransformer.Transform(filepath.ToSlash(path))) - } - return path -} - -// AppendablePath transforms the given path and also replaces colons with underscores to make it -// possible to append it to a base path on Windows -func AppendablePath(path string) string { - return strings.ReplaceAll(TransformPath(path), ":", "_") -} diff --git a/extractor/trap/labels.go b/extractor/trap/labels.go deleted file mode 100644 index 1e9bd2984..000000000 --- a/extractor/trap/labels.go +++ /dev/null @@ -1,232 +0,0 @@ -package trap - -import ( - "fmt" - "go/types" - - "github.com/github/codeql-go/extractor/util" -) - -// Label represents a label -type Label struct { - id string -} - -// InvalidLabel represents an uninitialized or otherwise invalid label -var InvalidLabel Label - -func (lbl *Label) String() string { - return lbl.id -} - -// Labeler is used to represent labels for a file. It is used to write -// associate objects with labels. -type Labeler struct { - tw *Writer - - nextid int - fileLabel Label - nodeLabels map[interface{}]Label // labels associated with AST nodes - scopeLabels map[*types.Scope]Label // labels associated with scopes - objectLabels map[types.Object]Label // labels associated with objects (that is, declared entities) - TypeLabels map[types.Type]Label // labels associated with types - keyLabels map[string]Label -} - -func newLabeler(tw *Writer) *Labeler { - return &Labeler{ - tw, - 10000, - InvalidLabel, - make(map[interface{}]Label), - make(map[*types.Scope]Label), - make(map[types.Object]Label), - make(map[types.Type]Label), - make(map[string]Label), - } -} - -func (l *Labeler) nextID() string { - var id = l.nextid - l.nextid++ - return fmt.Sprintf("#%d", id) -} - -// GlobalID associates a label with the given `key` and returns it -func (l *Labeler) GlobalID(key string) Label { - label, exists := l.keyLabels[key] - if !exists { - id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=@\"%s\"\n", id, escapeString(key)) - label = Label{id} - l.keyLabels[key] = label - } - return label -} - -// FileLabel returns the label for a file with path `path`. -func (l *Labeler) FileLabel() Label { - if l.fileLabel == InvalidLabel { - l.fileLabel = l.FileLabelFor(l.tw.path) - } - return l.fileLabel -} - -// FileLabelFor returns the label for the file for which the trap writer `tw` is associated -func (l *Labeler) FileLabelFor(path string) Label { - return l.GlobalID(util.EscapeTrapSpecialChars(path) + ";sourcefile") -} - -// LocalID associates a label with the given AST node `nd` and returns it -func (l *Labeler) LocalID(nd interface{}) Label { - label, exists := l.nodeLabels[nd] - if !exists { - label = l.FreshID() - l.nodeLabels[nd] = label - } - return label -} - -// FreshID creates a fresh label and returns it -func (l *Labeler) FreshID() Label { - id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=*\n", id) - return Label{id} -} - -// ScopeID associates a label with the given scope and returns it -func (l *Labeler) ScopeID(scope *types.Scope, pkg *types.Package) Label { - label, exists := l.scopeLabels[scope] - if !exists { - if scope == types.Universe { - label = l.GlobalID("universe;scope") - } else { - if pkg != nil && pkg.Scope() == scope { - // if this scope is the package scope - pkgLabel := l.GlobalID(util.EscapeTrapSpecialChars(pkg.Path()) + ";package") - label = l.GlobalID("{" + pkgLabel.String() + "};scope") - } else { - label = l.FreshID() - } - } - l.scopeLabels[scope] = label - } - return label -} - -// LookupObjectID looks up the label associated with the given object and returns it; if the object does not have -// a label yet, it tries to construct one based on its scope and/or name, and otherwise returns InvalidLabel -func (l *Labeler) LookupObjectID(object types.Object, typelbl Label) (Label, bool) { - label, exists := l.objectLabels[object] - if !exists { - if object.Parent() == nil { - // blank identifiers and the pseudo-package `.` (from `import . "..."` imports) can only be referenced - // once, so we can use a fresh label for them - if object.Name() == "_" || object.Name() == "." { - label = l.FreshID() - l.objectLabels[object] = label - return label, false - } - label = InvalidLabel - } else { - label, exists = l.ScopedObjectID(object, func() Label { return typelbl }) - } - } - return label, exists -} - -// ScopedObjectID associates a label with the given object and returns it, -// together with a flag indicating whether the object already had a label -// associated with it; the object must have a scope, since the scope's label is -// used to construct the label of the object. -// -// There is a special case for variables that are method receivers. When this is -// detected, we must construct a special label, as the variable can be reached -// from several files via the method. As the type label is required to construct -// the receiver object id, it is also required here. -func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) (Label, bool) { - label, exists := l.objectLabels[object] - if !exists { - scope := object.Parent() - if scope == nil { - panic(fmt.Sprintf("Object has no scope: %v :: %v.\n", object, - l.tw.Package.Fset.Position(object.Pos()))) - } else { - // associate method receiver objects to special keys, because those can be - // referenced from other files via their method - meth := findMethodWithGivenReceiver(object.Type(), object) - if meth == nil { - if pointerType, ok := object.Type().(*types.Pointer); ok { - meth = findMethodWithGivenReceiver(pointerType.Elem(), object) - } - } - - if meth != nil { - methlbl, _ := l.MethodID(meth, getTypeLabel()) - label, _ = l.ReceiverObjectID(object, methlbl) - } else { - scopeLbl := l.ScopeID(scope, object.Pkg()) - label = l.GlobalID(fmt.Sprintf("{%v},%s;object", scopeLbl, object.Name())) - } - } - l.objectLabels[object] = label - } - return label, exists -} - -func findMethodWithGivenReceiver(tp types.Type, object types.Object) *types.Func { - if namedType, ok := tp.(*types.Named); ok { - for i := 0; i < namedType.NumMethods(); i++ { - meth := namedType.Method(i) - if object == meth.Type().(*types.Signature).Recv() { - return meth - } - } - } - return nil -} - -// ReceiverObjectID associates a label with the given object and returns it, together with a flag indicating whether -// the object already had a label associated with it; the object must be the receiver of `methlbl`, since that label -// is used to construct the label of the object -func (l *Labeler) ReceiverObjectID(object types.Object, methlbl Label) (Label, bool) { - label, exists := l.objectLabels[object] - if !exists { - // if we can't, construct a special label - label = l.GlobalID(fmt.Sprintf("{%v},%s;receiver", methlbl, object.Name())) - l.objectLabels[object] = label - } - return label, exists -} - -// FieldID associates a label with the given field and returns it, together with -// a flag indicating whether the field already had a label associated with it; -// the field must belong to `structlbl`, since that label is used to construct -// the label of the field. When the field name is the blank identifier `_`, -// `idx` is used to generate a unique name. -func (l *Labeler) FieldID(field *types.Var, idx int, structlbl Label) (Label, bool) { - label, exists := l.objectLabels[field] - if !exists { - name := field.Name() - // there can be multiple fields with the blank identifier, so use index to - // distinguish them - if field.Name() == "_" { - name = fmt.Sprintf("_%d", idx) - } - label = l.GlobalID(fmt.Sprintf("{%v},%s;field", structlbl, name)) - l.objectLabels[field] = label - } - return label, exists -} - -// MethodID associates a label with the given method and returns it, together with a flag indicating whether -// the method already had a label associated with it; the method must belong to `recvtyplbl`, since that label -// is used to construct the label of the method -func (l *Labeler) MethodID(method types.Object, recvtyplbl Label) (Label, bool) { - label, exists := l.objectLabels[method] - if !exists { - label = l.GlobalID(fmt.Sprintf("{%v},%s;method", recvtyplbl, method.Name())) - l.objectLabels[method] = label - } - return label, exists -} diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go deleted file mode 100644 index 4c0911e24..000000000 --- a/extractor/trap/trapwriter.go +++ /dev/null @@ -1,168 +0,0 @@ -package trap - -import ( - "bufio" - "compress/gzip" - "errors" - "fmt" - "go/ast" - "go/types" - "io/ioutil" - "os" - "path/filepath" - "unicode/utf8" - - "github.com/github/codeql-go/extractor/srcarchive" - "golang.org/x/tools/go/packages" -) - -// A Writer provides methods for writing data to a TRAP file -type Writer struct { - zip *gzip.Writer - w *bufio.Writer - file *os.File - Labeler *Labeler - path string - trapFilePath string - Package *packages.Package - TypesOverride map[ast.Expr]types.Type - ObjectsOverride map[types.Object]types.Object -} - -func FileFor(path string) (string, error) { - trapFolder, err := trapFolder() - if err != nil { - return "", err - } - - return filepath.Join(trapFolder, srcarchive.AppendablePath(path)+".trap.gz"), nil -} - -// NewWriter creates a TRAP file for the given path and returns a writer for -// writing to it -func NewWriter(path string, pkg *packages.Package) (*Writer, error) { - trapFilePath, err := FileFor(path) - if err != nil { - return nil, err - } - trapFileDir := filepath.Dir(trapFilePath) - err = os.MkdirAll(trapFileDir, 0755) - if err != nil { - return nil, err - } - tmpFile, err := ioutil.TempFile(trapFileDir, filepath.Base(trapFilePath)) - if err != nil { - return nil, err - } - bufioWriter := bufio.NewWriter(tmpFile) - zipWriter := gzip.NewWriter(bufioWriter) - tw := &Writer{ - zipWriter, - bufioWriter, - tmpFile, - nil, - path, - trapFilePath, - pkg, - make(map[ast.Expr]types.Type), - make(map[types.Object]types.Object), - } - tw.Labeler = newLabeler(tw) - return tw, nil -} - -func trapFolder() (string, error) { - trapFolder := os.Getenv("CODEQL_EXTRACTOR_GO_TRAP_DIR") - if trapFolder == "" { - trapFolder = os.Getenv("TRAP_FOLDER") - } - if trapFolder == "" { - return "", errors.New("environment variable CODEQL_EXTRACTOR_GO_TRAP_DIR not set") - } - err := os.MkdirAll(trapFolder, 0755) - if err != nil { - return "", err - } - return trapFolder, nil -} - -// Close the underlying file writer -func (tw *Writer) Close() error { - err := tw.zip.Close() - if err != nil { - // return zip-close error, but ignore file-close error - tw.file.Close() - return err - } - err = tw.w.Flush() - if err != nil { - // throw away close error because write errors are likely to be more important - tw.file.Close() - return err - } - err = tw.file.Close() - if err != nil { - return err - } - return os.Rename(tw.file.Name(), tw.trapFilePath) -} - -// ForEachObject iterates over all objects labeled by this labeler, and invokes -// the provided callback with a writer for the trap file, the object, and its -// label. It returns true if any extra objects were labeled and false otherwise. -func (tw *Writer) ForEachObject(cb func(*Writer, types.Object, Label)) bool { - // copy the objects into an array so that our behaviour is deterministic even - // if `cb` adds any new objects - i := 0 - objects := make([]types.Object, len(tw.Labeler.objectLabels)) - for k := range tw.Labeler.objectLabels { - objects[i] = k - i++ - } - - for _, object := range objects { - cb(tw, object, tw.Labeler.objectLabels[object]) - } - - return len(tw.Labeler.objectLabels) != len(objects) -} - -const max_strlen = 1024 * 1024 - -func capStringLength(s string) string { - // if the UTF8-encoded string is longer than 1MiB, we truncate it - if len(s) > max_strlen { - // to ensure that the truncated string is valid UTF-8, we find the last byte at or - // before index max_strlen that starts a UTF-8 encoded character, and then cut off - // right before that byte - end := max_strlen - for ; !utf8.RuneStart(s[end]); end-- { - } - return s[0:end] - } - return s -} - -// Emit writes out a tuple of values for the given `table` -func (tw *Writer) Emit(table string, values []interface{}) error { - fmt.Fprintf(tw.zip, "%s(", table) - for i, value := range values { - if i > 0 { - fmt.Fprint(tw.zip, ", ") - } - switch value := value.(type) { - case Label: - fmt.Fprint(tw.zip, value.id) - case string: - fmt.Fprintf(tw.zip, "\"%s\"", escapeString(capStringLength(value))) - case int: - fmt.Fprintf(tw.zip, "%d", value) - case float64: - fmt.Fprintf(tw.zip, "%e", value) - default: - return errors.New("Cannot emit value") - } - } - fmt.Fprintf(tw.zip, ")\n") - return nil -} diff --git a/extractor/trap/trapwriter_test.go b/extractor/trap/trapwriter_test.go deleted file mode 100644 index 4fe1886b3..000000000 --- a/extractor/trap/trapwriter_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package trap - -import ( - "strings" - "testing" -) - -const ( - asciiChar = "*" - bmpChar = "\u2028" - nonBmpChar = "\U000101d0" -) - -func TestCapStringLength(t *testing.T) { - // test simple cases only involving ASCII characters - short := strings.Repeat(asciiChar, max_strlen-1) - if capStringLength(short) != short { - t.Errorf("Strings shorter than maximum length should not be truncated") - } - - short = strings.Repeat(asciiChar, max_strlen) - if capStringLength(short) != short { - t.Errorf("Strings no longer than maximum length should not be truncated") - } - - long := strings.Repeat(asciiChar, max_strlen+1) - if capStringLength(long) != long[0:max_strlen] { - t.Errorf("Strings longer than maximum length should be truncated") - } - - // test chopping off non-ASCII characters - prefix := strings.Repeat(asciiChar, max_strlen) - long = prefix + bmpChar - if capStringLength(long) != prefix { - t.Errorf("BMP character after max_strlen should be correctly chopped off") - } - - prefix = strings.Repeat(asciiChar, max_strlen) - long = prefix + nonBmpChar - if capStringLength(long) != prefix { - t.Errorf("Non-BMP character after max_strlen should be correctly chopped off") - } - - prefix = strings.Repeat(asciiChar, max_strlen-(len(bmpChar)-1)) - long = prefix + bmpChar - if capStringLength(long) != prefix { - t.Errorf("BMP character straddling max_strlen should be correctly chopped off") - } - - prefix = strings.Repeat(asciiChar, max_strlen-(len(nonBmpChar)-1)) - long = prefix + nonBmpChar - if capStringLength(long) != prefix { - t.Errorf("Non-BMP character straddling max_strlen should be correctly chopped off") - } - - // test preserving non-ASCII characters that just about fit - prefix = strings.Repeat(asciiChar, max_strlen-len(bmpChar)) - short = prefix + bmpChar - if capStringLength(short) != short { - t.Errorf("BMP character before max_strlen should be correctly preserved") - } - - prefix = strings.Repeat(asciiChar, max_strlen-len(nonBmpChar)) - short = prefix + nonBmpChar - if capStringLength(short) != short { - t.Errorf("Non-BMP character before max_strlen should be correctly preserved") - } -} diff --git a/extractor/trap/util.go b/extractor/trap/util.go deleted file mode 100644 index 2e32cfc04..000000000 --- a/extractor/trap/util.go +++ /dev/null @@ -1,9 +0,0 @@ -package trap - -import ( - "strings" -) - -func escapeString(s string) string { - return strings.Replace(s, "\"", "\"\"", -1) -} diff --git a/extractor/util/util.go b/extractor/util/util.go deleted file mode 100644 index 5725c03d5..000000000 --- a/extractor/util/util.go +++ /dev/null @@ -1,210 +0,0 @@ -package util - -import ( - "errors" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" -) - -var extractorPath string - -// Getenv retrieves the value of the environment variable named by the key. -// If that variable is not present, it iterates over the given aliases until -// it finds one that is. If none are present, the empty string is returned. -func Getenv(key string, aliases ...string) string { - val := os.Getenv(key) - if val != "" { - return val - } - for _, alias := range aliases { - val = os.Getenv(alias) - if val != "" { - return val - } - } - return "" -} - -// runGoList is a helper function for running go list with format `format` and flags `flags` on -// package `pkgpath`. -func runGoList(format string, pkgpath string, flags ...string) (string, error) { - return runGoListWithEnv(format, pkgpath, nil, flags...) -} - -func runGoListWithEnv(format string, pkgpath string, additionalEnv []string, flags ...string) (string, error) { - args := append([]string{"list", "-e", "-f", format}, flags...) - args = append(args, pkgpath) - cmd := exec.Command("go", args...) - cmd.Env = append(os.Environ(), additionalEnv...) - out, err := cmd.Output() - - if err != nil { - if err, ok := err.(*exec.ExitError); ok { - log.Printf("Warning: go list command failed, output below:\nstdout:\n%s\nstderr:\n%s\n", out, err.Stderr) - } else { - log.Printf("Warning: Failed to run go list: %s", err.Error()) - } - return "", err - } - - return strings.TrimSpace(string(out)), nil -} - -// GetModDir gets the absolute directory of the module containing the package with path -// `pkgpath`. It passes the `go list` the flags specified by `flags`. -func GetModDir(pkgpath string, flags ...string) string { - // enable module mode so that we can find a module root if it exists, even if go module support is - // disabled by a build - mod, err := runGoListWithEnv("{{.Module}}", pkgpath, []string{"GO111MODULE=on"}, flags...) - if err != nil || mod == "" { - // if the command errors or modules aren't being used, return the empty string - return "" - } - - modDir, err := runGoListWithEnv("{{.Module.Dir}}", pkgpath, []string{"GO111MODULE=on"}, flags...) - if err != nil { - return "" - } - - abs, err := filepath.Abs(modDir) - if err != nil { - log.Printf("Warning: unable to make %s absolute: %s", modDir, err.Error()) - return "" - } - return abs -} - -// GetPkgDir gets the absolute directory containing the package with path `pkgpath`. It passes the -// `go list` command the flags specified by `flags`. -func GetPkgDir(pkgpath string, flags ...string) string { - pkgDir, err := runGoList("{{.Dir}}", pkgpath, flags...) - if err != nil { - return "" - } - - abs, err := filepath.Abs(pkgDir) - if err != nil { - log.Printf("Warning: unable to make %s absolute: %s", pkgDir, err.Error()) - return "" - } - return abs -} - -// DepErrors checks there are any errors resolving dependencies for `pkgpath`. It passes the `go -// list` command the flags specified by `flags`. -func DepErrors(pkgpath string, flags ...string) bool { - out, err := runGoList("{{if .DepsErrors}}{{else}}error{{end}}", pkgpath, flags...) - if err != nil { - // if go list failed, assume dependencies are broken - return false - } - - return out != "" -} - -// FileExists tests whether the file at `filename` exists and is not a directory. -func FileExists(filename string) bool { - info, err := os.Stat(filename) - if err != nil && !os.IsNotExist(err) { - log.Printf("Unable to stat %s: %s\n", filename, err.Error()) - } - return err == nil && !info.IsDir() -} - -// DirExists tests whether `filename` exists and is a directory. -func DirExists(filename string) bool { - info, err := os.Stat(filename) - if err != nil && !os.IsNotExist(err) { - log.Printf("Unable to stat %s: %s\n", filename, err.Error()) - } - return err == nil && info.IsDir() -} - -func RunCmd(cmd *exec.Cmd) bool { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - in, _ := cmd.StdinPipe() - err := cmd.Start() - if err != nil { - log.Printf("Running %s failed, continuing anyway: %s\n", cmd.Path, err.Error()) - return false - } - in.Close() - err = cmd.Wait() - if err != nil { - log.Printf("Running %s failed, continuing anyway: %s\n", cmd.Path, err.Error()) - return false - } - - return true -} - -func getOsToolsSubdir() (string, error) { - platform, set := os.LookupEnv("CODEQL_PLATFORM") - if set && platform != "" { - return platform, nil - } - - switch runtime.GOOS { - case "darwin": - return "osx64", nil - case "linux": - return "linux64", nil - case "windows": - return "win64", nil - } - return "", errors.New("Unsupported OS: " + runtime.GOOS) -} - -func getExtractorDir() (string, error) { - extractorRoot := os.Getenv("CODEQL_EXTRACTOR_GO_ROOT") - if extractorRoot == "" { - log.Print("CODEQL_EXTRACTOR_GO_ROOT not set.\nThis binary should not be run manually; instead, use the CodeQL CLI or VSCode extension. See https://securitylab.github.com/tools/codeql.\n") - log.Print("Falling back to guess the root based on this executable's path.\n") - - mypath, err := os.Executable() - if err == nil { - return filepath.Dir(mypath), nil - } else { - return "", errors.New("CODEQL_EXTRACTOR_GO_ROOT not set, and could not determine path of this executable: " + err.Error()) - } - } - - osSubdir, err := getOsToolsSubdir() - if err != nil { - return "", err - } - - return filepath.Join(extractorRoot, "tools", osSubdir), nil -} - -func GetExtractorPath() (string, error) { - if extractorPath != "" { - return extractorPath, nil - } - - dirname, err := getExtractorDir() - if err != nil { - return "", err - } - extractorPath := filepath.Join(dirname, "go-extractor") - if runtime.GOOS == "windows" { - extractorPath = extractorPath + ".exe" - } - return extractorPath, nil -} - -func EscapeTrapSpecialChars(s string) string { - // Replace TRAP special characters with their HTML entities, as well as '&' to avoid ambiguity. - s = strings.ReplaceAll(s, "&", "&") - s = strings.ReplaceAll(s, "{", "{") - s = strings.ReplaceAll(s, "}", "}") - s = strings.ReplaceAll(s, "\"", """) - s = strings.ReplaceAll(s, "@", "@") - s = strings.ReplaceAll(s, "#", "#") - return s -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 25e2d87d3..000000000 --- a/go.mod +++ /dev/null @@ -1,13 +0,0 @@ -module github.com/github/codeql-go - -go 1.18 - -require ( - golang.org/x/mod v0.5.0 - golang.org/x/tools v0.1.5 -) - -require ( - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 57b82477e..000000000 --- a/go.sum +++ /dev/null @@ -1,28 +0,0 @@ -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/old-change-notes/1.23/analysis-go.md b/old-change-notes/1.23/analysis-go.md deleted file mode 100644 index 5710d7acf..000000000 --- a/old-change-notes/1.23/analysis-go.md +++ /dev/null @@ -1,15 +0,0 @@ -# Improvements to Go analysis - -## New queries - -| **Query** | **Tags** | **Purpose** | -|---------------------------------------------------------------------------|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| -| Clear-text logging of sensitive information (`go/clear-text-logging`) | security, external/cwe/cwe-312, external/cwe/cwe-315, external/cwe/cwe-359 | Highlights code that writes sensitive information to a log file, or to the console, without encryption or hashing. Results are shown on LGTM by default. | -| Open URL redirect (`go/unvalidated-url-redirection`) | security, external/cwe/cwe-601 | Highlights code that redirects to a URL that may be controlled by an attacker. Results are shown on LGTM by default. | - -## Changes to existing queries - -| **Query** | **Expected impact** | **Change** | -|-----------------------------------------------------|------------------------------|-----------------------------------------------------------| -| Expression has no effect (`go/useless-expression`) | Fewer false positive results | This query no longer flags calls to empty stub functions. | -| Hard-coded credentials (`go/hardcoded-credentials`) | Fewer false positive results | This query now recognizes more placeholder credentials. | diff --git a/old-change-notes/1.24/analysis-go.md b/old-change-notes/1.24/analysis-go.md deleted file mode 100644 index a9f38cb1e..000000000 --- a/old-change-notes/1.24/analysis-go.md +++ /dev/null @@ -1,39 +0,0 @@ -# Improvements to Go analysis - -## General improvements - -* You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`). -* Analysis of flow through fields and elements of arrays and slices has been improved, which may lead to more results from the security queries. -* Detection of test code has been improved. LGTM will not show alerts in test code by default. -* Go 1.14 library changes have been modeled. -* More sources of untrusted input as well as vulnerable sinks are modeled, which may lead to more results from the security queries. - -## New queries - -The CodeQL library for Go now contains a folder of simple "cookbook" queries that show how to access basic Go elements using the predicates defined by the standard library. They're intended to give you a starting point for your own experiments and to help you work out the best way to frame your questions using CodeQL. You can find them in the `examples/snippets` folder in the [CodeQL for Go repository](https://github.com/github/codeql-go/tree/main/ql/examples/snippets). - -| **Query** | **Tags** | **Purpose** | -|------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Bad check of redirect URL (`go/bad-redirect-check`) | correctness, security, external/cwe/cwe-601 | Highlights checks that ensure redirect URLs start with `/` but don't check for `//` or `/\`. Results are shown on LGTM by default. | -| Constant length comparison (`go/constant-length-comparison`) | correctness | Highlights code that checks the length of an array or slice against a constant before indexing it using a variable, suggesting a logic error. Results are shown on LGTM by default. | -| Disabled TLS certificate check (`go/disabled-certificate-check`) | security, external/cwe/295 | Highlights code that disables TLS certificate checking. Results are shown on LGTM by default. | -| Impossible interface nil check (`go/impossible-interface-nil-check`) | correctness | Highlights code that compares an interface value that cannot be `nil` to `nil`, suggesting a logic error. Results are shown on LGTM by default. | -| Incomplete URL scheme check (`go/incomplete-url-scheme-check`) | correctness, security, external/cwe/cwe-020 | Highlights checks for `javascript` URLs that do not take `data` or `vbscript` URLs into account. Results are shown on LGTM by default. | -| Potentially unsafe quoting (`go/unsafe-quoting`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-089, external/cwe/cwe-094 | Highlights code that constructs a quoted string literal containing data that may itself contain quotes. Results are shown on LGTM by default. | -| Size computation for allocation may overflow (`go/allocation-size-overflow`) | correctness, security, external/cwe/cwe-190 | Highlights code that computes the size of an allocation based on the size of a potentially large object. Results are shown on LGTM by default. | -| Uncontrolled data used in network request (`go/request-forgery`) | correctness, security, external/cwe/cwe-918 | Highlights code that uses uncontrolled user input to make a request. Results are shown on LGTM by default. | -| XPath injection (`go/xml/xpath-injection`) | security, external/cwe/cwe-643 | Highlights code that uses remote input in an XPath expression. Results are shown on LGTM by default. | - -## Changes to existing queries - -| **Query** | **Expected impact** | **Change** | -|-------------------------------------------------------------------------------|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Arbitrary file write during zip extraction ("zip slip") (`go/zipslip`) | Fewer false positive results | The query now excludes more cases where it is safe to use a path extractor from an archive. | -| Bitwise exclusive-or used like exponentiation (`go/mistyped-exponentiation`) | Fewer false positive results | The query now identifies when the value of an xor is assigned to a mask object, and excludes such results. | -| Command built from user-controlled sources (`go/command-injection`) | More results | The library models used by the query have been improved, allowing it to flag more potentially problematic cases, including sources that flow into shells, sudo, or programming-language interpreters as arguments. | -| Database query built from user-controlled sources (`go/sql-injection`) | More results | The library models used by the query have been improved, allowing it to flag more potentially problematic cases. | -| Identical operands (`go/redundant-operation`) | Fewer false positive results | The query no longer flags cases where the operands have the same value but are syntactically distinct, since this is usually intentional. | -| Incomplete regular expression for hostnames (`go/incomplete-hostname-regexp`) | More results | The query now flags unescaped dots before the TLD in a hostname regex. | -| Open URL redirect (`go/unvalidated-url-redirection`) | Fewer false positive results | The query now identifies some sources that are not attacker-controlled, and excludes results with such sources. | -| Reflected cross-site scripting (`go/reflected-xss`) | Fewer results | Untrusted input flowing into an HTTP header definition or into an `fmt.Fprintf` call with a constant prefix is no longer flagged, since it is in both cases often harmless. | -| Useless assignment to field (`go/useless-assignment-to-field`) | Fewer false positive results | The query now conservatively handles fields promoted through embedded pointer types. | diff --git a/old-change-notes/1.24/extractor-go.md b/old-change-notes/1.24/extractor-go.md deleted file mode 100644 index c51d47a78..000000000 --- a/old-change-notes/1.24/extractor-go.md +++ /dev/null @@ -1,19 +0,0 @@ -[[ condition: enterprise-only ]] - -# Improvements to Go analysis - -## Improvements to the autobuilder - -* When Makefiles or custom build scripts are present in the codebase, the autobuilder uses them to install dependencies. The build command - to invoke can be configured via `lgtm.yml`, or by setting the environment variable `CODEQL_EXTRACTOR_GO_BUILD_COMMAND`. -* The autobuilder now attempts to automatically detect when dependencies have been vendored and use `-mod=vendor` appropriately. - -## Changes to code extraction - -* The extractor now supports Go 1.14. -* In resource-constrained environments, the environment variable `CODEQL_EXTRACTOR_GO_MAX_GOROUTINES` can be used to limit the - number of parallel goroutines started by the extractor, which reduces CPU and memory requirements. The default value for this - variable is 32. -* The extractor now uses buffered i/o for writing database files, which reduces the amount of time taken for extraction. -* The extractor now compresses intermediate files used for constructing databases, which reduces the amount of disk space it requires. -* The extractor now supports extracting `go.mod` files, enabling queries on dependencies and their versions. diff --git a/old-change-notes/2020-04-30-syscall-functions.md b/old-change-notes/2020-04-30-syscall-functions.md deleted file mode 100644 index caa2cf566..000000000 --- a/old-change-notes/2020-04-30-syscall-functions.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Command built from user-controlled sources" has been improved to recognize methods from the `syscall` library, which may lead to more alerts. diff --git a/old-change-notes/2020-05-01-bad-redirect-check.md b/old-change-notes/2020-05-01-bad-redirect-check.md deleted file mode 100644 index 6ee5d0fb2..000000000 --- a/old-change-notes/2020-05-01-bad-redirect-check.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Bad redirect check" (`go/bad-redirect-check`) now requires that the checked variable is actually used in a redirect as opposed to relying on a name-based heuristic. This eliminates some false positive results, and adds more true positive results. diff --git a/old-change-notes/2020-05-01-macaron-model.md b/old-change-notes/2020-05-01-macaron-model.md deleted file mode 100644 index 056e7d093..000000000 --- a/old-change-notes/2020-05-01-macaron-model.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Basic support for the [Macaron](https://go-macaron.com/) HTTP library has been added, which may lead to more results from the security queries. \ No newline at end of file diff --git a/old-change-notes/2020-05-05-clear-text-logging.md b/old-change-notes/2020-05-05-clear-text-logging.md deleted file mode 100644 index ad9e97455..000000000 --- a/old-change-notes/2020-05-05-clear-text-logging.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Clear-text logging of sensitive information" has been improved to recognize more logging APIs, which may lead to more alerts. \ No newline at end of file diff --git a/old-change-notes/2020-05-05-mux-model.md b/old-change-notes/2020-05-05-mux-model.md deleted file mode 100644 index 4ca9d688d..000000000 --- a/old-change-notes/2020-05-05-mux-model.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Basic support for the [Mux](https://github.com/gorilla/mux/) HTTP library has been added, which - may lead to more results from the security queries. diff --git a/old-change-notes/2020-05-07-update-data-flow.md b/old-change-notes/2020-05-07-update-data-flow.md deleted file mode 100644 index 6d67c59b0..000000000 --- a/old-change-notes/2020-05-07-update-data-flow.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* The data-flow library has been improved, which affects and improves most security queries. In particular, - flow through functions involving nested field reads and writes is now modeled more fully. diff --git a/old-change-notes/2020-05-11-reflected-xss.md b/old-change-notes/2020-05-11-reflected-xss.md deleted file mode 100644 index 0d1f703c7..000000000 --- a/old-change-notes/2020-05-11-reflected-xss.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* The query "Reflected cross-site scripting" has been improved to recognize more cases where the - value should be considered to be safe, which should lead to fewer false positive results. diff --git a/old-change-notes/2020-05-12-tainted-path.md b/old-change-notes/2020-05-12-tainted-path.md deleted file mode 100644 index ccd78ed7d..000000000 --- a/old-change-notes/2020-05-12-tainted-path.md +++ /dev/null @@ -1,4 +0,0 @@ -lgtm,codescanning -* The queries "Uncontrolled data used in path expression" and "Arbitrary file write during zip - extraction ("zip slip")" have been improved to recognize more file APIs, which may lead to more - alerts. diff --git a/old-change-notes/2020-05-13-io-model.md b/old-change-notes/2020-05-13-io-model.md deleted file mode 100644 index 0e3531efe..000000000 --- a/old-change-notes/2020-05-13-io-model.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Modeling of the standard `io` library has been improved, which may lead to more results from the - security queries. diff --git a/old-change-notes/2020-05-18-redundant-recover.md b/old-change-notes/2020-05-18-redundant-recover.md deleted file mode 100644 index cca5e8fe4..000000000 --- a/old-change-notes/2020-05-18-redundant-recover.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query "Redundant call to recover" (`go/redundant-recover`) has been added. The query detects calls to `recover` that have no effect. diff --git a/old-change-notes/2020-05-20-mongodb-model.md b/old-change-notes/2020-05-20-mongodb-model.md deleted file mode 100644 index ff0a09d37..000000000 --- a/old-change-notes/2020-05-20-mongodb-model.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Modeling of the `go.mongodb.org/mongo-driver/mongo` package has been added, which may lead to more - results from the security queries. diff --git a/old-change-notes/2020-05-20-request-forgery-sanitizers.md b/old-change-notes/2020-05-20-request-forgery-sanitizers.md deleted file mode 100644 index cb1fdcb5a..000000000 --- a/old-change-notes/2020-05-20-request-forgery-sanitizers.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Uncontrolled data used in network request" is now more precise, which may reduce the number of false positives. diff --git a/old-change-notes/2020-05-22-websocket-model.md b/old-change-notes/2020-05-22-websocket-model.md deleted file mode 100644 index 5ce24528e..000000000 --- a/old-change-notes/2020-05-22-websocket-model.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Modeling of several WebSocket libraries has been added, which may lead to more results from the - security queries. diff --git a/old-change-notes/2020-05-29-open-redirect.md b/old-change-notes/2020-05-29-open-redirect.md deleted file mode 100644 index 182a001ae..000000000 --- a/old-change-notes/2020-05-29-open-redirect.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Open URL redirect" (`go/unvalidated-url-redirection`) now recognizes values returned by method `http.Request.FormValue` as possibly user controlled, allowing it to flag more true positive results. diff --git a/old-change-notes/2020-06-11-build-tracing.md b/old-change-notes/2020-06-11-build-tracing.md deleted file mode 100644 index b500742db..000000000 --- a/old-change-notes/2020-06-11-build-tracing.md +++ /dev/null @@ -1,5 +0,0 @@ -lgtm,codescanning -* The Go extractor now supports build tracing, allowing users to supply a build command when - creating databases with the CodeQL CLI or via configuration. It currently only supports projects - that use Go modules. To opt-in, set the environment variable `CODEQL_EXTRACTOR_GO_BUILD_TRACING` - to `on`, or supply a build command. diff --git a/old-change-notes/2020-06-12-more-dependency-extraction.md b/old-change-notes/2020-06-12-more-dependency-extraction.md deleted file mode 100644 index 76523ced9..000000000 --- a/old-change-notes/2020-06-12-more-dependency-extraction.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The extractor now attempts to extract the AST of all dependencies that are related to the packages passed explicitly on the commandline, which is determined by using the module root or, if not using modules, the directory containing the source for those packages. In particular, this means if a package passed to the extractor depends on another package inside the same module, the dependency's AST will now be extracted. diff --git a/old-change-notes/2020-06-16-email-injection.md b/old-change-notes/2020-06-16-email-injection.md deleted file mode 100644 index 63abd57f2..000000000 --- a/old-change-notes/2020-06-16-email-injection.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Email injection" (`go/email-injection`) has been moved out of the experimental folder. The query detects when untrusted input can be incorporated directly into an email. diff --git a/old-change-notes/2020-06-17-reflected-xss-refinements.md b/old-change-notes/2020-06-17-reflected-xss-refinements.md deleted file mode 100644 index 7cb277327..000000000 --- a/old-change-notes/2020-06-17-reflected-xss-refinements.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* The query "Reflected cross-site scripting" has been improved to more correctly determine whether - an HTML mime type will be sniffed, which should lead to more accurate results. diff --git a/old-change-notes/2020-06-19-call-graph.md b/old-change-notes/2020-06-19-call-graph.md deleted file mode 100644 index 891ddda62..000000000 --- a/old-change-notes/2020-06-19-call-graph.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Resolution of method calls through interfaces has been improved, resulting in more precise call-graph information, which in turn may eliminate false positives from the security queries. diff --git a/old-change-notes/2020-06-19-cyclic-field-lookup.md b/old-change-notes/2020-06-19-cyclic-field-lookup.md deleted file mode 100644 index 04901d575..000000000 --- a/old-change-notes/2020-06-19-cyclic-field-lookup.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A bug has been fixed that could cause the analysis not to terminate in the presence of cycles through embedded struct fields. \ No newline at end of file diff --git a/old-change-notes/2020-06-19-switch-block-without-test.md b/old-change-notes/2020-06-19-switch-block-without-test.md deleted file mode 100644 index 17f20f684..000000000 --- a/old-change-notes/2020-06-19-switch-block-without-test.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A bug has been fixed that could cause the incorrect analysis of control flow around switch statements. diff --git a/old-change-notes/2020-06-24-clear-text-logging.md b/old-change-notes/2020-06-24-clear-text-logging.md deleted file mode 100644 index 6dd26dbb1..000000000 --- a/old-change-notes/2020-06-24-clear-text-logging.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Clear-text logging of sensitive information" has been improved to recognize more sources of sensitive data, which may lead to more alerts. The query is now also more precise, which may reduce the number of false positives. diff --git a/old-change-notes/2020-06-24-open-redirect.md b/old-change-notes/2020-06-24-open-redirect.md deleted file mode 100644 index 3916ca733..000000000 --- a/old-change-notes/2020-06-24-open-redirect.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Open URL redirect" (`go/unvalidated-url-redirection`) now recognizes more problematic fields of `URL` objects, allowing it to flag more results. \ No newline at end of file diff --git a/old-change-notes/2020-06-26-taint-model-tar-zip.md b/old-change-notes/2020-06-26-taint-model-tar-zip.md deleted file mode 100644 index 10cdaa41b..000000000 --- a/old-change-notes/2020-06-26-taint-model-tar-zip.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Modeling of the `archive/tar` and `archive/zip` packages has been added, which may lead to more - results from the security queries. diff --git a/old-change-notes/2020-06-26-taint-through-range.md b/old-change-notes/2020-06-26-taint-through-range.md deleted file mode 100644 index 55d870d1c..000000000 --- a/old-change-notes/2020-06-26-taint-through-range.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Taint tracking through `range` statements has been improved, which may cause more results from the security queries. diff --git a/old-change-notes/2020-07-06-repo-with-file-url-origin.md b/old-change-notes/2020-07-06-repo-with-file-url-origin.md deleted file mode 100644 index f84d4c995..000000000 --- a/old-change-notes/2020-07-06-repo-with-file-url-origin.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A bug has been fixed that caused the autobuilder to not work on repositories with a `file://` URL as `origin`. diff --git a/old-change-notes/2020-07-06-tolerate-more-unreachable-returns.md b/old-change-notes/2020-07-06-tolerate-more-unreachable-returns.md deleted file mode 100644 index 766c767e7..000000000 --- a/old-change-notes/2020-07-06-tolerate-more-unreachable-returns.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Unreachable statement" (`go/unreachable-statement`) now tolerates more unreachable return statements, which can often be required in Go following a function call that cannot return. Newly tolerated statements include `return true`, `return MyStruct{0, true}`, and any return when the return value has type `error`. This eliminates some nuisance results. diff --git a/old-change-notes/2020-07-07-missing-error-check.md b/old-change-notes/2020-07-07-missing-error-check.md deleted file mode 100644 index 9202e3bc9..000000000 --- a/old-change-notes/2020-07-07-missing-error-check.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* New query "Missing error check" (`go/missing-error-check`) added. This checks for dangerous pointer dereferences when an accompanying error value returned from a call has not been checked. diff --git a/old-change-notes/2020-07-15-insecure-tls.md b/old-change-notes/2020-07-15-insecure-tls.md deleted file mode 100644 index 8cacfa5b2..000000000 --- a/old-change-notes/2020-07-15-insecure-tls.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query "Insecure TLS configuration" (`go/insecure-tls`) is promoted from experimental status. This checks for use of insecure SSL/TLS versions and cipher suites. diff --git a/old-change-notes/2020-07-22-ssh-host-checking.md b/old-change-notes/2020-07-22-ssh-host-checking.md deleted file mode 100644 index 7f83626bf..000000000 --- a/old-change-notes/2020-07-22-ssh-host-checking.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query "Use of insecure HostKeyCallback implementation" (`go/insecure-hostkeycallback`) is promoted from experimental status. This checks for insecurely omitting SSH host-key verification. diff --git a/old-change-notes/2020-07-28-library-models.md b/old-change-notes/2020-07-28-library-models.md deleted file mode 100644 index 41a0de5c0..000000000 --- a/old-change-notes/2020-07-28-library-models.md +++ /dev/null @@ -1,9 +0,0 @@ -lgtm,codescanning -* Basic support for the [Go-restful](https://github.com/emicklei/go-restful) HTTP library has been added, which - may lead to more results from the security queries. -* Basic support for the [Gorm](https://github.com/go-gorm/gorm) ORM library has been added (specifically, its SQL statement building facilities), which - may lead to more results from the security queries. -* Basic support for the [Sqlx](https://github.com/jmoiron/sqlx) database access library has been added, which - may lead to more results from the security queries. -* Basic support for the [Json-iterator](https://github.com/json-iterator/go) JSON library has been added, which - may lead to more results from the security queries. diff --git a/old-change-notes/2020-08-05-incorrect-integer-conversion.md b/old-change-notes/2020-08-05-incorrect-integer-conversion.md deleted file mode 100644 index 258d8427f..000000000 --- a/old-change-notes/2020-08-05-incorrect-integer-conversion.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query "Incorrect integer conversion" (`go/incorrect-integer-conversion`) is promoted from experimental status. This checks for parsing a string to an integer and then assigning it to an integer type of a smaller bit size. diff --git a/old-change-notes/2020-08-07-negative-length-check.md b/old-change-notes/2020-08-07-negative-length-check.md deleted file mode 100644 index 38db6f38d..000000000 --- a/old-change-notes/2020-08-07-negative-length-check.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Query "Redundant check for negative value" (`go/negative-length-check`) has been expanded to consider unsigned integers, along - with the return values of `len` and `cap` which it already handled. It has also been renamed to match its expanded role. diff --git a/old-change-notes/2020-08-18-oauth2.md b/old-change-notes/2020-08-18-oauth2.md deleted file mode 100644 index aa4b566a3..000000000 --- a/old-change-notes/2020-08-18-oauth2.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Use of constant `state` value in OAuth 2.0 URL" (`go/constant-oauth2-state`) has been promoted from experimental status. This checks for use of a constant state value in generating an OAuth2 redirect URL, which may open the way for a CSRF attack. diff --git a/old-change-notes/2020-08-19-gin-model.md b/old-change-notes/2020-08-19-gin-model.md deleted file mode 100644 index 03a834323..000000000 --- a/old-change-notes/2020-08-19-gin-model.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Basic support for the [Gin](https://github.com/gin-gonic/gin) HTTP library has been added (extending UntrustedFlowSource), which may lead to more results from the security queries. diff --git a/old-change-notes/2020-08-27-protobufs.md b/old-change-notes/2020-08-27-protobufs.md deleted file mode 100644 index 2628fd12d..000000000 --- a/old-change-notes/2020-08-27-protobufs.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Taint is now propagated across protocol buffer ("protobuf") marshalling and unmarshalling operations. This may result in more results from existing queries where the protocol buffer format is used. diff --git a/old-change-notes/2020-08-28-allocation-size-overflow-sanitizers.md b/old-change-notes/2020-08-28-allocation-size-overflow-sanitizers.md deleted file mode 100644 index 8876401c7..000000000 --- a/old-change-notes/2020-08-28-allocation-size-overflow-sanitizers.md +++ /dev/null @@ -1,4 +0,0 @@ -lgtm,codescanning -* The query "Size computation for allocation may overflow" has been improved to recognize more - cases where the value should be considered to be safe, which should lead to fewer false - positive results. diff --git a/old-change-notes/2020-09-10-gorm-model-improved.md b/old-change-notes/2020-09-10-gorm-model-improved.md deleted file mode 100644 index 10b99296e..000000000 --- a/old-change-notes/2020-09-10-gorm-model-improved.md +++ /dev/null @@ -1,4 +0,0 @@ -lgtm,codescanning -* Support for the [GORM](https://github.com/go-gorm/gorm) ORM library (specifically, its SQL - statement building facilities) has been improved, which may lead to more results from the - security queries. diff --git a/old-change-notes/2020-09-10-xss-false-positives.md b/old-change-notes/2020-09-10-xss-false-positives.md deleted file mode 100644 index dd04bc4ba..000000000 --- a/old-change-notes/2020-09-10-xss-false-positives.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Reflected cross-site scripting" (`go/reflected-xss`) now recognizes more cases of JSON marshaled data, which cannot serve as a vector for an XSS attack. This may reduce false-positive results for this query. diff --git a/old-change-notes/2020-09-14-split-string-sanitizer.md b/old-change-notes/2020-09-14-split-string-sanitizer.md deleted file mode 100644 index 970da425e..000000000 --- a/old-change-notes/2020-09-14-split-string-sanitizer.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Splitting a string by whitespace or a colon is now considered sanitizing by the `go/clear-text-logging` query, because this is frequently used to split a username and password or other secret. diff --git a/old-change-notes/2020-09-15-chi.md b/old-change-notes/2020-09-15-chi.md deleted file mode 100644 index f8674951c..000000000 --- a/old-change-notes/2020-09-15-chi.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the Chi web framework diff --git a/old-change-notes/2020-09-17-echo.md b/old-change-notes/2020-09-17-echo.md deleted file mode 100644 index 75576b8c4..000000000 --- a/old-change-notes/2020-09-17-echo.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the Echo web framework diff --git a/old-change-notes/2020-09-22-suspicious-character-in-regexp.md b/old-change-notes/2020-09-22-suspicious-character-in-regexp.md deleted file mode 100644 index 1cb06313e..000000000 --- a/old-change-notes/2020-09-22-suspicious-character-in-regexp.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query `go/suspicious-character-in-regex` has been added. The query flags uses of `\b` and `\a` in regular expressions, where a character class was likely intended. diff --git a/old-change-notes/2020-09-23-stdlib.md b/old-change-notes/2020-09-23-stdlib.md deleted file mode 100644 index 2b30973e7..000000000 --- a/old-change-notes/2020-09-23-stdlib.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Add/improve taint-tracking models for 63 Go standard library packages. This means that all queries that track tainted data may produce more results; these include queries scanning for cross-site scripting vulnerabilities and SQL injection vulnerabilities among others. diff --git a/old-change-notes/2020-10-01-gomod-extraction.md b/old-change-notes/2020-10-01-gomod-extraction.md deleted file mode 100644 index ca0c0c72d..000000000 --- a/old-change-notes/2020-10-01-gomod-extraction.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The extractor now only extracts go.mod files belonging to extracted packages. In particular, vendored go.mod files will no longer be extracted unless the vendored package is explicitly passed to the extractor. This will remove unexpected `GoModExpr` and similar expressions seen by queries. diff --git a/old-change-notes/2020-10-01-stack-trace-exposure.md b/old-change-notes/2020-10-01-stack-trace-exposure.md deleted file mode 100644 index 35437a059..000000000 --- a/old-change-notes/2020-10-01-stack-trace-exposure.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query `go/stack-trace-exposure` has been added. The query flags exposure of a stack trace to a remote party. diff --git a/old-change-notes/2020-10-12-old-context-package.md b/old-change-notes/2020-10-12-old-context-package.md deleted file mode 100644 index 3e40ba064..000000000 --- a/old-change-notes/2020-10-12-old-context-package.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the `golang.org/x/net/context` package, which was already supported under its modern standard-library name `context`. diff --git a/old-change-notes/2020-10-12-x-net-html.md b/old-change-notes/2020-10-12-x-net-html.md deleted file mode 100644 index 4644ecf9e..000000000 --- a/old-change-notes/2020-10-12-x-net-html.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* Added partial support for the `golang.org/x/net/html` package, modeling tainted data flow from a retrieved HTML document to its attributes and other data. -* Modeled more ways of writing data to an `net/http.ResponseWriter`. This may produce more results from queries such as `go/reflected-xss` which look for data flowing to an HTTP response. diff --git a/old-change-notes/2020-10-14-allocation-overflow-accuracy.md b/old-change-notes/2020-10-14-allocation-overflow-accuracy.md deleted file mode 100644 index 44e2ec955..000000000 --- a/old-change-notes/2020-10-14-allocation-overflow-accuracy.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The accuracy of the `go/allocation-size-overflow` query was improved, excluding more false-positives in which a small array could be mistaken for one of unbounded size. diff --git a/old-change-notes/2020-10-14-spew.md b/old-change-notes/2020-10-14-spew.md deleted file mode 100644 index 29103258c..000000000 --- a/old-change-notes/2020-10-14-spew.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the Spew deep pretty-printing framework. This may cause the `go/clear-text-logging` query to return more results when sensitive data is exposed using this library. diff --git a/old-change-notes/2020-10-19-revel.md b/old-change-notes/2020-10-19-revel.md deleted file mode 100644 index 3683c95ac..000000000 --- a/old-change-notes/2020-10-19-revel.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added basic support for the Revel web framework. diff --git a/old-change-notes/2020-11-03-underscore-assigns.md b/old-change-notes/2020-11-03-underscore-assigns.md deleted file mode 100644 index d8f7accfe..000000000 --- a/old-change-notes/2020-11-03-underscore-assigns.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Fixed a bug that meant partially-ignored parallel assignments, such as `x, _ := a, b`, could produce an incorrect control-flow graph. diff --git a/old-change-notes/2020-11-04-unsafe-unzip-symlink.md b/old-change-notes/2020-11-04-unsafe-unzip-symlink.md deleted file mode 100644 index 88a13019d..000000000 --- a/old-change-notes/2020-11-04-unsafe-unzip-symlink.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query `go/unsafe-unzip-symlink` has been added. The query checks for extracting symbolic links from an archive without using `filepath.EvalSymlinks`. This could lead to a file being written outside the destination directory. diff --git a/old-change-notes/2020-11-09-suspicious-character-in-regexp-improvement.md b/old-change-notes/2020-11-09-suspicious-character-in-regexp-improvement.md deleted file mode 100644 index 3e5c9fce5..000000000 --- a/old-change-notes/2020-11-09-suspicious-character-in-regexp-improvement.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Suspicious characters in a regular expression" has been improved to recognize raw string literals, which should lead to fewer false positives. diff --git a/old-change-notes/2020-11-11-stored-command.md b/old-change-notes/2020-11-11-stored-command.md deleted file mode 100644 index fed234b2d..000000000 --- a/old-change-notes/2020-11-11-stored-command.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query "Command built from stored data" (`go/stored-command`) has been added. The query detects command executions that contain data from a database or a similar possibly user-controllable source. diff --git a/old-change-notes/2020-11-11-stored-xss.md b/old-change-notes/2020-11-11-stored-xss.md deleted file mode 100644 index fb9f87713..000000000 --- a/old-change-notes/2020-11-11-stored-xss.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A new query "Stored cross-site scripting" (`go/stored-xss`) has been added. The query detects HTTP request responses that contain data from a database or a similar possibly user-controllable source. diff --git a/old-change-notes/2020-11-12-zipslip-sanitizers.md b/old-change-notes/2020-11-12-zipslip-sanitizers.md deleted file mode 100644 index 880660ae4..000000000 --- a/old-change-notes/2020-11-12-zipslip-sanitizers.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Improved recongition of sanitizer functions for the `go/zipslip` query. This may reduce false-positives (but also perhaps false-negatives) when application code attempts to check a zip header entry does not contain an illegal path traversal attempt. diff --git a/old-change-notes/2020-11-19-dataflow-edges.md b/old-change-notes/2020-11-19-dataflow-edges.md deleted file mode 100644 index 827b33ce4..000000000 --- a/old-change-notes/2020-11-19-dataflow-edges.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Fixed a bug that meant data-flow through a checked typecast (e.g. `cast, ok = x.(*Type)`) could be missed. diff --git a/old-change-notes/2020-11-27-external-api.md b/old-change-notes/2020-11-27-external-api.md deleted file mode 100644 index b65b4da85..000000000 --- a/old-change-notes/2020-11-27-external-api.md +++ /dev/null @@ -1,4 +0,0 @@ -lgtm,codescanning -* A new query "Untrusted data passed to external API" (`go/untrusted-data-to-external-api`) has been added. The query reports external APIs that use untrusted data. This query is designed primarily to help identify which APIs may be relevant for security analysis of this application. -* A new query "Untrusted data passed to unknown external API" (`go/untrusted-data-to-unknown-external-api`) has been added. The query reports external APIs that use untrusted data and which are not already known to be safe. This query is designed primarily to help identify which APIs may be relevant for security analysis of this application. -* A new query "Frequency counts for external APIs that are used with untrusted data" (`go/count-untrusted-data-external-api`) has been added. The query reports external APIs that use untrusted data. It displays the same results as "Untrusted data passed to external API" (`go/untrusted-data-to-external-api`) but in a table. diff --git a/old-change-notes/2020-11-27-guarding-functions.md b/old-change-notes/2020-11-27-guarding-functions.md deleted file mode 100644 index 5a51d59ef..000000000 --- a/old-change-notes/2020-11-27-guarding-functions.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Improved ability to recognise a sanitizing function (for example, `func f(s string) bool { return isClean(s) }`). This may reduce false-positives for any query employing a sanitizing test. diff --git a/old-change-notes/2020-11-30-evanphx-json-patch.md b/old-change-notes/2020-11-30-evanphx-json-patch.md deleted file mode 100644 index 5c0e17bde..000000000 --- a/old-change-notes/2020-11-30-evanphx-json-patch.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Support for the [json-patch](https://github.com/evanphx/json-patch/) library has been added, which may lead to more results from the security queries. diff --git a/old-change-notes/2020-12-01-fmt-errorf.md b/old-change-notes/2020-12-01-fmt-errorf.md deleted file mode 100644 index e90c59676..000000000 --- a/old-change-notes/2020-12-01-fmt-errorf.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Recognised function `fmt.Errorf` to always return non-nil strings. This may reduce false-positives that depend on a function possibly returning nil. diff --git a/old-change-notes/2020-12-08-beego.md b/old-change-notes/2020-12-08-beego.md deleted file mode 100644 index 59084d625..000000000 --- a/old-change-notes/2020-12-08-beego.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the Beego web framework diff --git a/old-change-notes/2020-12-08-k8s-io-apimachinery-pkg-runtime.md b/old-change-notes/2020-12-08-k8s-io-apimachinery-pkg-runtime.md deleted file mode 100644 index b4d6ac7e7..000000000 --- a/old-change-notes/2020-12-08-k8s-io-apimachinery-pkg-runtime.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Support for the [k8s.io/apimachinery/pkg/runtime](https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime) library has been added, which may lead to more results from the security queries. diff --git a/old-change-notes/2020-12-09-clear-text-logging-source.md b/old-change-notes/2020-12-09-clear-text-logging-source.md deleted file mode 100644 index 7ba2113ba..000000000 --- a/old-change-notes/2020-12-09-clear-text-logging-source.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The query "Clear-text logging of sensitive information" has been improved to recognize `SecretInterface` from `k8s.io/client-go/kubernetes/typed/core/v1` as a source of sensitive data, which may lead to more alerts. diff --git a/old-change-notes/2020-12-14-insecure-randomness.md b/old-change-notes/2020-12-14-insecure-randomness.md deleted file mode 100644 index 9e016703e..000000000 --- a/old-change-notes/2020-12-14-insecure-randomness.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query "Use of insufficient randomness as the key of a cryptographic algorithm" (`go/insecure-randomness`) is promoted from experimental status. This checks for use of an insecure random number generator in a security component. diff --git a/old-change-notes/2020-12-15-beego-orm.md b/old-change-notes/2020-12-15-beego-orm.md deleted file mode 100644 index 79fd312ed..000000000 --- a/old-change-notes/2020-12-15-beego-orm.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the ORM subpackage of the Beego web framework diff --git a/old-change-notes/2020-12-18-goproxy.md b/old-change-notes/2020-12-18-goproxy.md deleted file mode 100644 index e56d542a7..000000000 --- a/old-change-notes/2020-12-18-goproxy.md +++ /dev/null @@ -1,5 +0,0 @@ -lgtm,codescanning -* Added support for the `github.com/elazarl/goproxy` package. -* The query "Incomplete regular expression for hostnames" has been improved to recognize some cases - when the regexp in question is guarding an HTTP error response, which will lead to fewer false - positives. diff --git a/old-change-notes/2020-12-23-regexp-anchors.md b/old-change-notes/2020-12-23-regexp-anchors.md deleted file mode 100644 index 4574cf1c1..000000000 --- a/old-change-notes/2020-12-23-regexp-anchors.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query `go/regex/missing-regexp-anchor` now recognizes the start- and end-of-text anchors `\A` and `\z`. This reduces false-positives relating to unanchored expressions. diff --git a/old-change-notes/2021-01-07-gokit-sources.md b/old-change-notes/2021-01-07-gokit-sources.md deleted file mode 100644 index 3f544e14f..000000000 --- a/old-change-notes/2021-01-07-gokit-sources.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Models for gokit request sources have been added as an opt-in feature; import `semmle.go.frameworks.GoKit` in a query to enable these sources. diff --git a/old-change-notes/2021-01-08-git-as-interpreter.md b/old-change-notes/2021-01-08-git-as-interpreter.md deleted file mode 100644 index adcd9cafa..000000000 --- a/old-change-notes/2021-01-08-git-as-interpreter.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added `git` as a potentially-exploitable command interpreter for the purposes of the `go/command-injection` query. Because some of its options can cause it to execute an arbitrary command, unsanitized user data can be dangerous to include in its argument list. Such cases will now be flagged as an alert. diff --git a/old-change-notes/2021-01-12-model-couchbase.md b/old-change-notes/2021-01-12-model-couchbase.md deleted file mode 100644 index 77e0973cd..000000000 --- a/old-change-notes/2021-01-12-model-couchbase.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for [the offical Couchbase Go SDK library](https://github.com/couchbase/gocb), v1 and v2. The `go/sql-injection` query (which also handles non-SQL databases such as Couchbase) will now identify Couchbase queries built from untrusted external input. diff --git a/old-change-notes/2021-01-21-default-sanitizer-guard.md b/old-change-notes/2021-01-21-default-sanitizer-guard.md deleted file mode 100644 index 2ee9b0d9e..000000000 --- a/old-change-notes/2021-01-21-default-sanitizer-guard.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* An equality comparison with a constant value now sanitizes the other value. This was already the case in XSS queries, but it now applies in all queries involving tainted data flow. This should lead to fewer false positive results. diff --git a/old-change-notes/2021-02-02-constant-comparison-sanitizer-guard.md b/old-change-notes/2021-02-02-constant-comparison-sanitizer-guard.md deleted file mode 100644 index 0442a7f9c..000000000 --- a/old-change-notes/2021-02-02-constant-comparison-sanitizer-guard.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* A function which compares a value with a list of constants now acts as a sanitizer guard. This should lead to fewer false positive results. diff --git a/old-change-notes/2021-02-09-html-templates.md b/old-change-notes/2021-02-09-html-templates.md deleted file mode 100644 index d742ea463..000000000 --- a/old-change-notes/2021-02-09-html-templates.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Improved our modeling of Go's builtin `html/template` package to understand that these templates provide context-sensitive escaping of HTML and Javascript special characters. This may reduce false-positives seen by the `go/reflected-xss` query, as well as other queries for which HTML escaping is relevant. diff --git a/old-change-notes/2021-02-10-cfg-equality-panic-edges.md b/old-change-notes/2021-02-10-cfg-equality-panic-edges.md deleted file mode 100644 index 4a4e41b89..000000000 --- a/old-change-notes/2021-02-10-cfg-equality-panic-edges.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Improved the Go control-flow graph to exclude more edges representing panics due to comparisons when the types of the compared values indicate a panic is impossible (for example, comparing integers cannot panic). This may reduce false-positives or false-negatives for any query for which control-flow is relevant. diff --git a/old-change-notes/2021-02-10-yaml.md b/old-change-notes/2021-02-10-yaml.md deleted file mode 100644 index 3a23f42a0..000000000 --- a/old-change-notes/2021-02-10-yaml.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the [gopkg.in/yaml](https://pkg.go.dev/gopkg.in/yaml.v3) package, which may lead to more results from the security queries. diff --git a/old-change-notes/2021-02-11-zap.md b/old-change-notes/2021-02-11-zap.md deleted file mode 100644 index a318f05ac..000000000 --- a/old-change-notes/2021-02-11-zap.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for [the Zap logging framework](https://pkg.go.dev/go.uber.org/zap). This may cause the `go/clear-text-logging` query to return more results when sensitive data is exposed using this library. diff --git a/old-change-notes/2021-02-15-logrus-updated.md b/old-change-notes/2021-02-15-logrus-updated.md deleted file mode 100644 index eacc43ced..000000000 --- a/old-change-notes/2021-02-15-logrus-updated.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Modeling of the `Logrus` logging library has been improved. This may cause the `go/clear-text-logging` query to return more results when sensitive data is exposed using this library. diff --git a/old-change-notes/2021-02-18-go-116.md b/old-change-notes/2021-02-18-go-116.md deleted file mode 100644 index 353b5afa1..000000000 --- a/old-change-notes/2021-02-18-go-116.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The extractor now supports Go 1.16 and the new `io/fs` library that was introduced. diff --git a/old-change-notes/2021-03-05-dataflow-promoted-fields.md b/old-change-notes/2021-03-05-dataflow-promoted-fields.md deleted file mode 100644 index 54b5d7805..000000000 --- a/old-change-notes/2021-03-05-dataflow-promoted-fields.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The data-flow library has been improved to represent reads and writes of promoted fields correctly, which may lead to more alerts. diff --git a/old-change-notes/2021-03-16-html-tracing.md b/old-change-notes/2021-03-16-html-tracing.md deleted file mode 100644 index 664e5f312..000000000 --- a/old-change-notes/2021-03-16-html-tracing.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Support for extracting HTML files has been added, alongside support for Raw Revel templates. diff --git a/old-change-notes/2021-03-16-nethttp-updated.md b/old-change-notes/2021-03-16-nethttp-updated.md deleted file mode 100644 index f9138f67f..000000000 --- a/old-change-notes/2021-03-16-nethttp-updated.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the `Transport.RoundTrip` method in `net/http`. diff --git a/old-change-notes/2021-04-19-http-request-taint-flow.md b/old-change-notes/2021-04-19-http-request-taint-flow.md deleted file mode 100644 index 08eb761e0..000000000 --- a/old-change-notes/2021-04-19-http-request-taint-flow.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* `net/http.Request` and `mime/multipart.Part`'s models have been improved. `Request`'s error returns are no longer considered tainted, and `Part`'s methods propagate taint (for example, the `Part.FileName()` of a tainted `Part` is itself tainted). This should lead to more accurate results from any query where `Request` or `Part` methods occurred in a taint-flow path. diff --git a/old-change-notes/2021-04-20-tuple-types.md b/old-change-notes/2021-04-20-tuple-types.md deleted file mode 100644 index a4db5df19..000000000 --- a/old-change-notes/2021-04-20-tuple-types.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Fixed a bug where data flow was not correctly computed through two-value index expressions (for example, `got, ok := myMap[someIndex]`). This may lead to extra results from any dataflow query when an index expression would form part of an important dataflow path. diff --git a/old-change-notes/2021-05-06-xorm.md b/old-change-notes/2021-05-06-xorm.md deleted file mode 100644 index 0398530b9..000000000 --- a/old-change-notes/2021-05-06-xorm.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added support for the `xorm.io/xorm` package diff --git a/old-change-notes/2021-07-28-insufficient-key-size.md b/old-change-notes/2021-07-28-insufficient-key-size.md deleted file mode 100644 index d3880bee7..000000000 --- a/old-change-notes/2021-07-28-insufficient-key-size.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Query "Use of a weak cryptographic key" (`go/insufficient-key-size`) is promoted from experimental status. This checks that any RSA keys which are generated have a size of at least 2048 bits. diff --git a/old-change-notes/2021-08-17-go-117.md b/old-change-notes/2021-08-17-go-117.md deleted file mode 100644 index 640f771b5..000000000 --- a/old-change-notes/2021-08-17-go-117.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* The extractor now supports Go 1.17 features and models the changed libraries. diff --git a/old-change-notes/2021-08-23-getPrimaryQlClasses.md b/old-change-notes/2021-08-23-getPrimaryQlClasses.md deleted file mode 100644 index 30b00908a..000000000 --- a/old-change-notes/2021-08-23-getPrimaryQlClasses.md +++ /dev/null @@ -1,2 +0,0 @@ -lgtm,codescanning -* Added `AstNode.getPrimaryQlClasses()` predicate, which gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. diff --git a/old-change-notes/2021-10-27-insufficient-key-size-sanitizer.md b/old-change-notes/2021-10-27-insufficient-key-size-sanitizer.md deleted file mode 100644 index 5cd7a616e..000000000 --- a/old-change-notes/2021-10-27-insufficient-key-size-sanitizer.md +++ /dev/null @@ -1,3 +0,0 @@ -lgtm,codescanning -* The query "Use of a weak cryptographic key" has been improved to recognize more cases where the - key size should be considered to be safe, which should lead to fewer false positive results. diff --git a/ql/config/legacy-support/qlpack.yml b/ql/config/legacy-support/qlpack.yml deleted file mode 100644 index cff1d02b2..000000000 --- a/ql/config/legacy-support/qlpack.yml +++ /dev/null @@ -1,3 +0,0 @@ -name: legacy-libraries-go -version: 0.0.0 -libraryPathDependencies: codeql-go diff --git a/ql/config/suites/lgtm/go-alerts-lgtm b/ql/config/suites/lgtm/go-alerts-lgtm deleted file mode 100644 index 17b8ee5c4..000000000 --- a/ql/config/suites/lgtm/go-alerts-lgtm +++ /dev/null @@ -1,3 +0,0 @@ -# DO NOT EDIT -# This is a stub file. The actual suite of queries to run is generated -# automatically based on query precision and severity. diff --git a/ql/config/suites/lgtm/go-lgtm b/ql/config/suites/lgtm/go-lgtm deleted file mode 100644 index fa96641e2..000000000 --- a/ql/config/suites/lgtm/go-lgtm +++ /dev/null @@ -1,3 +0,0 @@ -@import "go-alerts-lgtm" -@import "go-metrics-lgtm" -@import "go-util-lgtm" diff --git a/ql/config/suites/lgtm/go-metrics-lgtm b/ql/config/suites/lgtm/go-metrics-lgtm deleted file mode 100644 index a295f30c0..000000000 --- a/ql/config/suites/lgtm/go-metrics-lgtm +++ /dev/null @@ -1,2 +0,0 @@ -+ go-queries/Metrics/FLinesOfCode.ql: /Metrics/Files - @_namespace com.lgtm/go-queries diff --git a/ql/config/suites/lgtm/go-util-lgtm b/ql/config/suites/lgtm/go-util-lgtm deleted file mode 100644 index 908e157d2..000000000 --- a/ql/config/suites/lgtm/go-util-lgtm +++ /dev/null @@ -1,6 +0,0 @@ -+ go-queries/definitions.ql - @_namespace com.lgtm/go-queries -+ go-queries/AlertSuppression.ql - @_namespace com.lgtm/go-queries -+ go-queries/filters/ClassifyFiles.ql - @_namespace com.lgtm/go-queries diff --git a/ql/docs/experimental.md b/ql/docs/experimental.md deleted file mode 100644 index bef435d66..000000000 --- a/ql/docs/experimental.md +++ /dev/null @@ -1,37 +0,0 @@ -# Experimental CodeQL queries and libraries - -In addition to our standard CodeQL queries and libraries, this repository may also contain queries and libraries of a more experimental nature. Experimental queries and libraries can be improved incrementally and may eventually reach a sufficient maturity to be included in our standard libraries and queries. - -Experimental queries and libraries may not be actively maintained as the standard libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings. - -## Requirements - -1. **Directory structure** - - - Experimental queries and libraries are stored in the `ql/src/experimental` subdirectory, and any corresponding tests in `ql/test/experimental`. - - The structure of an `experimental` subdirectory mirrors the structure of standard queries and libraries (or tests) in the parent directory. - -2. **Query metadata** - - - The query `@id` must not clash with any other queries in the repository. - - The query must have a `@name` and `@description` to explain its purpose. - - The query must have a `@kind` and `@problem.severity` as required by CodeQL tools. - - For details, see the [guide on query metadata](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md). - -3. **Formatting** - - - The queries and libraries must be [autoformatted](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-codeql-for-visual-studio-code/). - -4. **Compilation** - - - Compilation of the query and any associated libraries and tests must be resilient to future development of the standard libraries. This means that the functionality cannot use internal APIs, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`. - - The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations). - -5. **Results** - - - The query must have at least one true positive result on some revision of a real project. - -## Non-requirements - -Other criteria typically required for our standard queries and libraries are not required for experimental queries and libraries. In particular, fully disciplined query [metadata](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md), query [help](https://github.com/github/codeql/blob/main/docs/query-help-style-guide.md), tests, a low false positive rate and performance tuning are not required (but nonetheless recommended). diff --git a/ql/examples/.project b/ql/examples/.project deleted file mode 100644 index 45114efbc..000000000 --- a/ql/examples/.project +++ /dev/null @@ -1,12 +0,0 @@ - - - go-examples - - - - - - - com.semmle.plugin.qdt.core.qlnature - - diff --git a/ql/examples/.qlpath b/ql/examples/.qlpath deleted file mode 100644 index ac27c7630..000000000 --- a/ql/examples/.qlpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - /go-queries - - /go-queries/go.dbscheme - - go - - diff --git a/ql/examples/qlpack.lock.yml b/ql/examples/qlpack.lock.yml deleted file mode 100644 index 06dd07fc7..000000000 --- a/ql/examples/qlpack.lock.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -dependencies: {} -compiled: false -lockVersion: 1.0.0 diff --git a/ql/examples/qlpack.yml b/ql/examples/qlpack.yml deleted file mode 100644 index bfe19ffd6..000000000 --- a/ql/examples/qlpack.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: codeql/go-examples -groups: - - go - - examples -dependencies: - codeql/go-all: "*" diff --git a/ql/examples/queries.xml b/ql/examples/queries.xml deleted file mode 100644 index 6a456b21d..000000000 --- a/ql/examples/queries.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ql/examples/snippets/calltobuiltin.ql b/ql/examples/snippets/calltobuiltin.ql deleted file mode 100644 index 5ff20e563..000000000 --- a/ql/examples/snippets/calltobuiltin.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Call to built-in function - * @description Finds calls to the built-in `len` function. - * @id go/examples/calltolen - * @tags call - * function - * len - * built-in - */ - -import go - -from DataFlow::CallNode call -where call = Builtin::len().getACall() -select call diff --git a/ql/examples/snippets/calltofunction.ql b/ql/examples/snippets/calltofunction.ql deleted file mode 100644 index 5bbe28b52..000000000 --- a/ql/examples/snippets/calltofunction.ql +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @name Call to library function - * @description Finds calls to "fmt.Println". - * @id go/examples/calltoprintln - * @tags call - * function - * println - */ - -import go - -from Function println, DataFlow::CallNode call -where - println.hasQualifiedName("fmt", "Println") and - call = println.getACall() -select call diff --git a/ql/examples/snippets/calltomethod.ql b/ql/examples/snippets/calltomethod.ql deleted file mode 100644 index eebfc707f..000000000 --- a/ql/examples/snippets/calltomethod.ql +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @name Call to method - * @description Finds calls to the `Get` method of type `Header` from the `net/http` package. - * @id go/examples/calltoheaderget - * @tags call - * function - * net/http - * Header - * strings - */ - -import go - -from Method get, DataFlow::CallNode call -where - get.hasQualifiedName("net/http", "Header", "Get") and - call = get.getACall() -select call diff --git a/ql/examples/snippets/constant.ql b/ql/examples/snippets/constant.ql deleted file mode 100644 index 3627efb67..000000000 --- a/ql/examples/snippets/constant.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Compile-time constant - * @description Finds compile-time constants with value zero. - * @id go/examples/zeroconstant - * @tags expression - * numeric value - * constant - */ - -import go - -from DataFlow::Node zero -where zero.getNumericValue() = 0 -select zero diff --git a/ql/examples/snippets/emptythen.ql b/ql/examples/snippets/emptythen.ql deleted file mode 100644 index 97a810e38..000000000 --- a/ql/examples/snippets/emptythen.ql +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @name If statements with empty then branch - * @description Finds 'if' statements where the 'then' branch is - * an empty block statement - * @id go/examples/emptythen - * @tags if - * then - * empty - * conditional - * branch - * statement - */ - -import go - -from IfStmt i -where i.getThen().getNumStmt() = 0 -select i diff --git a/ql/examples/snippets/fieldread.ql b/ql/examples/snippets/fieldread.ql deleted file mode 100644 index aef9bc2f4..000000000 --- a/ql/examples/snippets/fieldread.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Field read - * @description Finds code that reads `Request.Method`. - * @id go/examples/readofrequestmethod - * @tags field - * read - */ - -import go - -from Field reqm, Read read -where - reqm.hasQualifiedName("net/http", "Request", "Method") and - read = reqm.getARead() -select read diff --git a/ql/examples/snippets/fieldwrite.ql b/ql/examples/snippets/fieldwrite.ql deleted file mode 100644 index b9374f4ef..000000000 --- a/ql/examples/snippets/fieldwrite.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Field write - * @description Finds assignments to field `Status` of type `Response` from package `net/http`. - * @id go/examples/responsestatus - * @tags net/http - * field write - */ - -import go - -from Field status, Write write -where - status.hasQualifiedName("net/http", "Response", "Status") and - write = status.getAWrite() -select write, write.getRhs() diff --git a/ql/examples/snippets/function.ql b/ql/examples/snippets/function.ql deleted file mode 100644 index 559dd34c2..000000000 --- a/ql/examples/snippets/function.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Function - * @description Finds functions called "main". - * @id go/examples/mainfunction - * @tags function - * main - */ - -import go - -from Function main -where main.getName() = "main" -select main diff --git a/ql/examples/snippets/incompleteswitchoverenum.ql b/ql/examples/snippets/incompleteswitchoverenum.ql deleted file mode 100644 index b201e5508..000000000 --- a/ql/examples/snippets/incompleteswitchoverenum.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Incomplete switch over enum - * @description A switch statement of enum type should explicitly reference each - * of the members of that enum. - * @kind problem - * @id go/examples/incomplete-switch - */ - -import go - -from ExpressionSwitchStmt ss, DeclaredConstant c, NamedType t -where - t.getUnderlyingType() instanceof IntegerType and - t = ss.getExpr().getType() and - c.getType() = t and - forall(CaseClause case | case = ss.getACase() | not case = c.getAReference().getParent()) -select ss, "This switch statement is not exhaustive: missing $@", c, c.getName() diff --git a/ql/examples/snippets/nilcheck.ql b/ql/examples/snippets/nilcheck.ql deleted file mode 100644 index cb82f1461..000000000 --- a/ql/examples/snippets/nilcheck.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Comparison with nil - * @description Finds comparisons with nil. - * @id go/examples/nilcheck - * @tags comparison - * nil - */ - -import go - -from DataFlow::EqualityTestNode eq, DataFlow::Node nd, DataFlow::Node nil -where - nil = Builtin::nil().getARead() and - eq.eq(_, nd, nil) -select eq diff --git a/ql/examples/snippets/param.ql b/ql/examples/snippets/param.ql deleted file mode 100644 index 4340e018f..000000000 --- a/ql/examples/snippets/param.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Parameter - * @description Finds parameters of type "ResponseWriter" from package "net/http". - * @id go/examples/responseparam - * @tags parameter - */ - -import go - -from Parameter req -where req.getType().hasQualifiedName("net/http", "ResponseWriter") -select req diff --git a/ql/examples/snippets/pointertype.ql b/ql/examples/snippets/pointertype.ql deleted file mode 100644 index b21f03f24..000000000 --- a/ql/examples/snippets/pointertype.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Type - * @description Finds pointer type `*Request` from package `net/http`. - * @id go/examples/requestptrtype - * @tags net/http - * type - */ - -import go - -from Type reqtp, PointerType reqptrtp -where - reqtp.hasQualifiedName("net/http", "Request") and - reqptrtp.getBaseType() = reqtp -select reqptrtp diff --git a/ql/examples/snippets/receiver.ql b/ql/examples/snippets/receiver.ql deleted file mode 100644 index 511ef1314..000000000 --- a/ql/examples/snippets/receiver.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Receiver variable - * @description Finds receiver variables of pointer type. - * @id go/examples/pointerreceiver - * @tags receiver variable - */ - -import go - -from ReceiverVariable recv -where recv.getType() instanceof PointerType -select recv diff --git a/ql/examples/snippets/result.ql b/ql/examples/snippets/result.ql deleted file mode 100644 index 1241142bd..000000000 --- a/ql/examples/snippets/result.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Result variable - * @description Finds result variables of type "error". - * @id go/examples/errresult - * @tags result variable - */ - -import go - -from ResultVariable err -where err.getType() = Builtin::error().getType() -select err diff --git a/ql/examples/snippets/type.ql b/ql/examples/snippets/type.ql deleted file mode 100644 index 60861c72b..000000000 --- a/ql/examples/snippets/type.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Type - * @description Finds type `Request` from package `net/http`. - * @id go/examples/requesttype - * @tags net/http - * type - */ - -import go - -from Type request -where request.hasQualifiedName("net/http", "Request") -select request diff --git a/ql/examples/snippets/typeinfo.ql b/ql/examples/snippets/typeinfo.ql deleted file mode 100644 index b5c229434..000000000 --- a/ql/examples/snippets/typeinfo.ql +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @name Type information - * @description Finds code elements of type `*Request` from package `net/http`. - * @id go/examples/requests - * @tags net/http - * types - */ - -import go - -from Type reqtp, PointerType reqptrtp, DataFlow::Node req -where - reqtp.hasQualifiedName("net/http", "Request") and - reqptrtp.getBaseType() = reqtp and - req.getType() = reqptrtp -select req diff --git a/ql/examples/snippets/updateinloop.ql b/ql/examples/snippets/updateinloop.ql deleted file mode 100644 index f047f16da..000000000 --- a/ql/examples/snippets/updateinloop.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Increment statements in loops - * @description Finds increment statements that are nested in a loop - * @id go/examples/updateinloop - * @tags nesting - * increment - */ - -import go - -from IncStmt s, LoopStmt l -where s.getParent+() = l -select s, l diff --git a/ql/examples/snippets/variable.ql b/ql/examples/snippets/variable.ql deleted file mode 100644 index 28bb49345..000000000 --- a/ql/examples/snippets/variable.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Variable - * @description Finds variables called "err". - * @id go/examples/errvariable - * @tags variable - * err - */ - -import go - -from Variable err -where err.getName() = "err" -select err, err.getDeclaration() diff --git a/ql/examples/snippets/varread.ql b/ql/examples/snippets/varread.ql deleted file mode 100644 index 87282146d..000000000 --- a/ql/examples/snippets/varread.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Variable read - * @description Finds code that reads a variable called `err`. - * @id go/examples/readoferr - * @tags variable read - */ - -import go - -from Variable err, Read read -where - err.getName() = "err" and - read = err.getARead() -select read diff --git a/ql/examples/snippets/varwrite.ql b/ql/examples/snippets/varwrite.ql deleted file mode 100644 index 189e915c1..000000000 --- a/ql/examples/snippets/varwrite.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Variable write - * @description Finds assignments to variables named "err". - * @id go/examples/errwrite - * @tags variable write - */ - -import go - -from Variable err, Write write -where - err.getName() = "err" and - write = err.getAWrite() -select write, write.getRhs() diff --git a/ql/examples/snippets/zerocheck.ql b/ql/examples/snippets/zerocheck.ql deleted file mode 100644 index 467dae712..000000000 --- a/ql/examples/snippets/zerocheck.ql +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @name Comparison with zero - * @description Finds comparisons between an unsigned value and zero. - * @id go/examples/unsignedgez - * @tags comparison - * unsigned - */ - -import go - -from DataFlow::RelationalComparisonNode cmp, DataFlow::Node unsigned, DataFlow::Node zero -where - zero.getNumericValue() = 0 and - unsigned.getType().getUnderlyingType() instanceof UnsignedIntegerType and - cmp.leq(_, zero, unsigned, 0) -select cmp, unsigned diff --git a/ql/lib/CHANGELOG.md b/ql/lib/CHANGELOG.md deleted file mode 100644 index 940f3e172..000000000 --- a/ql/lib/CHANGELOG.md +++ /dev/null @@ -1,45 +0,0 @@ -## 0.1.2 - -### New Features - -* Go 1.18 generics are now extracted and can be explored using the new CodeQL classes `TypeParamDecl`, `GenericFunctionInstantiationExpr`, `GenericTypeInstantiationExpr`, `TypeSetTerm`, and `TypeSetLiteralType`, as well as using new predicates defined on the existing `InterfaceType`. Class- and predicate-level documentation can be found in the [Go CodeQL library reference](https://codeql.github.com/codeql-standard-libraries/go/). - -## 0.1.1 - -### Minor Analysis Improvements - -* The method predicate `getACalleeIncludingExternals` on `DataFlow::CallNode` and the function `viableCallable` in `DataFlowDispatch` now also work for calls to functions via a variable, where the function can be determined using local flow. - -## 0.1.0 - -### Minor Analysis Improvements - -* Fixed a bug where dataflow steps were ignored if both ends were inside the initialiser routine of a file-level variable. - -## 0.0.12 - -## 0.0.11 - -## 0.0.10 - -## 0.0.9 - -## 0.0.8 - -## 0.0.7 - -### Deprecated APIs - -* The `codeql/go-upgrades` CodeQL pack has been removed. All database upgrade scripts have been merged into the `codeql/go-all` CodeQL pack. - -### Bug Fixes - -* `Function`'s predicate `getACall` now returns more results in some situations. It now always returns callers that may call a method indirectly via an interface method that it implements. Previously this only happened if the method was in the source code being analysed. - -## 0.0.6 - -## 0.0.5 - -## 0.0.4 - -## 0.0.3 diff --git a/ql/lib/Customizations.qll b/ql/lib/Customizations.qll deleted file mode 100644 index 127840de9..000000000 --- a/ql/lib/Customizations.qll +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Contains customizations to the standard library. - * - * This module is imported by `go.qll`, so any customizations defined here automatically - * apply to all queries. - * - * Typical examples of customizations include adding new subclasses of abstract classes such as - * `FileSystemAccess`, or the `Source` and `Sink` classes associated with the security queries - * to model frameworks that are not covered by the standard library. - */ - -import go diff --git a/ql/lib/change-notes/released/0.0.10.md b/ql/lib/change-notes/released/0.0.10.md deleted file mode 100644 index 979029c01..000000000 --- a/ql/lib/change-notes/released/0.0.10.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.10 diff --git a/ql/lib/change-notes/released/0.0.11.md b/ql/lib/change-notes/released/0.0.11.md deleted file mode 100644 index eba254bd5..000000000 --- a/ql/lib/change-notes/released/0.0.11.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.11 diff --git a/ql/lib/change-notes/released/0.0.12.md b/ql/lib/change-notes/released/0.0.12.md deleted file mode 100644 index a8e2f0b46..000000000 --- a/ql/lib/change-notes/released/0.0.12.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.12 diff --git a/ql/lib/change-notes/released/0.0.3.md b/ql/lib/change-notes/released/0.0.3.md deleted file mode 100644 index e47c9f570..000000000 --- a/ql/lib/change-notes/released/0.0.3.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.3 diff --git a/ql/lib/change-notes/released/0.0.4.md b/ql/lib/change-notes/released/0.0.4.md deleted file mode 100644 index 3268fefb2..000000000 --- a/ql/lib/change-notes/released/0.0.4.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.4 diff --git a/ql/lib/change-notes/released/0.0.5.md b/ql/lib/change-notes/released/0.0.5.md deleted file mode 100644 index 259776640..000000000 --- a/ql/lib/change-notes/released/0.0.5.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.5 diff --git a/ql/lib/change-notes/released/0.0.6.md b/ql/lib/change-notes/released/0.0.6.md deleted file mode 100644 index 7cad4d986..000000000 --- a/ql/lib/change-notes/released/0.0.6.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.6 diff --git a/ql/lib/change-notes/released/0.0.7.md b/ql/lib/change-notes/released/0.0.7.md deleted file mode 100644 index 338327d31..000000000 --- a/ql/lib/change-notes/released/0.0.7.md +++ /dev/null @@ -1,9 +0,0 @@ -## 0.0.7 - -### Deprecated APIs - -* The `codeql/go-upgrades` CodeQL pack has been removed. All database upgrade scripts have been merged into the `codeql/go-all` CodeQL pack. - -### Bug Fixes - -* `Function`'s predicate `getACall` now returns more results in some situations. It now always returns callers that may call a method indirectly via an interface method that it implements. Previously this only happened if the method was in the source code being analysed. diff --git a/ql/lib/change-notes/released/0.0.8.md b/ql/lib/change-notes/released/0.0.8.md deleted file mode 100644 index bc5efa50e..000000000 --- a/ql/lib/change-notes/released/0.0.8.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.8 diff --git a/ql/lib/change-notes/released/0.0.9.md b/ql/lib/change-notes/released/0.0.9.md deleted file mode 100644 index a64559b3a..000000000 --- a/ql/lib/change-notes/released/0.0.9.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.9 diff --git a/ql/lib/change-notes/released/0.1.0.md b/ql/lib/change-notes/released/0.1.0.md deleted file mode 100644 index 6e0621538..000000000 --- a/ql/lib/change-notes/released/0.1.0.md +++ /dev/null @@ -1,5 +0,0 @@ -## 0.1.0 - -### Minor Analysis Improvements - -* Fixed a bug where dataflow steps were ignored if both ends were inside the initialiser routine of a file-level variable. diff --git a/ql/lib/change-notes/released/0.1.1.md b/ql/lib/change-notes/released/0.1.1.md deleted file mode 100644 index 724e8e4cd..000000000 --- a/ql/lib/change-notes/released/0.1.1.md +++ /dev/null @@ -1,5 +0,0 @@ -## 0.1.1 - -### Minor Analysis Improvements - -* The method predicate `getACalleeIncludingExternals` on `DataFlow::CallNode` and the function `viableCallable` in `DataFlowDispatch` now also work for calls to functions via a variable, where the function can be determined using local flow. diff --git a/ql/lib/change-notes/released/0.1.2.md b/ql/lib/change-notes/released/0.1.2.md deleted file mode 100644 index 6dfa74f5b..000000000 --- a/ql/lib/change-notes/released/0.1.2.md +++ /dev/null @@ -1,5 +0,0 @@ -## 0.1.2 - -### New Features - -* Go 1.18 generics are now extracted and can be explored using the new CodeQL classes `TypeParamDecl`, `GenericFunctionInstantiationExpr`, `GenericTypeInstantiationExpr`, `TypeSetTerm`, and `TypeSetLiteralType`, as well as using new predicates defined on the existing `InterfaceType`. Class- and predicate-level documentation can be found in the [Go CodeQL library reference](https://codeql.github.com/codeql-standard-libraries/go/). diff --git a/ql/lib/codeql-pack.release.yml b/ql/lib/codeql-pack.release.yml deleted file mode 100644 index 6abd14b1e..000000000 --- a/ql/lib/codeql-pack.release.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -lastReleaseVersion: 0.1.2 diff --git a/ql/lib/definitions.ql b/ql/lib/definitions.ql deleted file mode 100644 index 46e28d9fe..000000000 --- a/ql/lib/definitions.ql +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @name Jump-to-definition links - * @description Generates use-definition pairs that provide the data - * for jump-to-definition in the code viewer. - * @kind definitions - * @id go/jump-to-definition - */ - -import go - -from Ident def, Ident use, Entity e -where - use.uses(e) and - def.declares(e) -select use, def, "V" diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme deleted file mode 100644 index 90fa7836e..000000000 --- a/ql/lib/go.dbscheme +++ /dev/null @@ -1,547 +0,0 @@ -/** Auto-generated dbscheme; do not edit. */ - - -/** Duplicate code **/ - -duplicateCode( - unique int id : @duplication, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -similarCode( - unique int id : @similarity, - varchar(900) relativePath : string ref, - int equivClass : int ref); - -@duplication_or_similarity = @duplication | @similarity; - -tokens( - int id : @duplication_or_similarity ref, - int offset : int ref, - int beginLine : int ref, - int beginColumn : int ref, - int endLine : int ref, - int endColumn : int ref); - -/** External data **/ - -externalData( - int id : @externalDataElement, - varchar(900) path : string ref, - int column: int ref, - varchar(900) value : string ref -); - -snapshotDate(unique date snapshotDate : date ref); - -sourceLocationPrefix(varchar(900) prefix : string ref); - - -/* - * XML Files - */ - -xmlEncoding( - unique int id: @file ref, - string encoding: string ref -); - -xmlDTDs( - unique int id: @xmldtd, - string root: string ref, - string publicId: string ref, - string systemId: string ref, - int fileid: @file ref -); - -xmlElements( - unique int id: @xmlelement, - string name: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int fileid: @file ref -); - -xmlAttrs( - unique int id: @xmlattribute, - int elementid: @xmlelement ref, - string name: string ref, - string value: string ref, - int idx: int ref, - int fileid: @file ref -); - -xmlNs( - int id: @xmlnamespace, - string prefixName: string ref, - string URI: string ref, - int fileid: @file ref -); - -xmlHasNs( - int elementId: @xmlnamespaceable ref, - int nsId: @xmlnamespace ref, - int fileid: @file ref -); - -xmlComments( - unique int id: @xmlcomment, - string text: string ref, - int parentid: @xmlparent ref, - int fileid: @file ref -); - -xmlChars( - unique int id: @xmlcharacters, - string text: string ref, - int parentid: @xmlparent ref, - int idx: int ref, - int isCDATA: int ref, - int fileid: @file ref -); - -@xmlparent = @file | @xmlelement; -@xmlnamespaceable = @xmlelement | @xmlattribute; - -xmllocations( - int xmlElement: @xmllocatable ref, - int location: @location_default ref -); - -@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; - -compilations(unique int id: @compilation, string cwd: string ref); - -#keyset[id, num] -compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); - -#keyset[id, num, kind] -compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); - -diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); - -compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); - -#keyset[id, num] -compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); - -diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, - string full_error_message: string ref, int location: @location ref); - -locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, - int endLine: int ref, int endColumn: int ref); - -numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); - -files(unique int id: @file, string name: string ref); - -folders(unique int id: @folder, string name: string ref); - -containerparent(int parent: @container ref, unique int child: @container ref); - -has_location(unique int locatable: @locatable ref, int location: @location ref); - -#keyset[parent, idx] -comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); - -comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); - -doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); - -#keyset[parent, idx] -exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); - -literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); - -constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); - -fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); - -typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); - -#keyset[parent, idx] -stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); - -#keyset[parent, idx] -decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); - -#keyset[parent, idx] -specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); - -scopes(unique int id: @scope, int kind: int ref); - -scopenesting(unique int inner: @scope ref, int outer: @scope ref); - -scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); - -objects(unique int id: @object, int kind: int ref, string name: string ref); - -objectscopes(unique int object: @object ref, int scope: @scope ref); - -objecttypes(unique int object: @object ref, int tp: @type ref); - -methodreceivers(unique int method: @object ref, int receiver: @object ref); - -fieldstructs(unique int field: @object ref, int struct: @structtype ref); - -methodhosts(int method: @object ref, int host: @namedtype ref); - -defs(int ident: @ident ref, int object: @object ref); - -uses(int ident: @ident ref, int object: @object ref); - -types(unique int id: @type, int kind: int ref); - -type_of(unique int expr: @expr ref, int tp: @type ref); - -typename(unique int tp: @type ref, string name: string ref); - -key_type(unique int map: @maptype ref, int tp: @type ref); - -element_type(unique int container: @containertype ref, int tp: @type ref); - -base_type(unique int ptr: @pointertype ref, int tp: @type ref); - -underlying_type(unique int named: @namedtype ref, int tp: @type ref); - -#keyset[parent, index] -component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); - -array_length(unique int tp: @arraytype ref, string len: string ref); - -type_objects(unique int tp: @type ref, int object: @object ref); - -packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); - -#keyset[parent, idx] -modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); - -#keyset[parent, idx] -modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); - -#keyset[package, idx] -errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, - string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); - -has_ellipsis(int id: @callorconversionexpr ref); - -variadic(int id: @signaturetype ref); - -#keyset[parent, idx] -typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref, - int parent: @typeparamparentobject ref, int idx: int ref); - -@container = @file | @folder; - -@locatable = @xmllocatable | @node | @localscope; - -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent - | @scopenode | @comment_group | @comment; - -@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; - -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; - -@modexprparent = @file | @modexpr; - -@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; - -@stmtparent = @funcdef | @stmt | @decl; - -@declparent = @file | @declstmt; - -@typeparamdeclparent = @funcdecl | @typespec; - -@funcdef = @funclit | @funcdecl; - -@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; - -@location = @location_default; - -@sourceline = @locatable; - -case @comment.kind of - 0 = @slashslashcomment -| 1 = @slashstarcomment; - -case @expr.kind of - 0 = @badexpr -| 1 = @ident -| 2 = @ellipsis -| 3 = @intlit -| 4 = @floatlit -| 5 = @imaglit -| 6 = @charlit -| 7 = @stringlit -| 8 = @funclit -| 9 = @compositelit -| 10 = @parenexpr -| 11 = @selectorexpr -| 12 = @indexexpr -| 13 = @genericfunctioninstantiationexpr -| 14 = @generictypeinstantiationexpr -| 15 = @sliceexpr -| 16 = @typeassertexpr -| 17 = @callorconversionexpr -| 18 = @starexpr -| 19 = @keyvalueexpr -| 20 = @arraytypeexpr -| 21 = @structtypeexpr -| 22 = @functypeexpr -| 23 = @interfacetypeexpr -| 24 = @maptypeexpr -| 25 = @typesetliteralexpr -| 26 = @plusexpr -| 27 = @minusexpr -| 28 = @notexpr -| 29 = @complementexpr -| 30 = @derefexpr -| 31 = @addressexpr -| 32 = @arrowexpr -| 33 = @lorexpr -| 34 = @landexpr -| 35 = @eqlexpr -| 36 = @neqexpr -| 37 = @lssexpr -| 38 = @leqexpr -| 39 = @gtrexpr -| 40 = @geqexpr -| 41 = @addexpr -| 42 = @subexpr -| 43 = @orexpr -| 44 = @xorexpr -| 45 = @mulexpr -| 46 = @quoexpr -| 47 = @remexpr -| 48 = @shlexpr -| 49 = @shrexpr -| 50 = @andexpr -| 51 = @andnotexpr -| 52 = @sendchantypeexpr -| 53 = @recvchantypeexpr -| 54 = @sendrcvchantypeexpr -| 55 = @errorexpr; - -@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; - -@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; - -@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; - -@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; - -@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; - -@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; - -@logicalunaryexpr = @notexpr; - -@bitwiseunaryexpr = @complementexpr; - -@arithmeticunaryexpr = @plusexpr | @minusexpr; - -@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; - -@logicalbinaryexpr = @lorexpr | @landexpr; - -@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; - -@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; - -@shiftexpr = @shlexpr | @shrexpr; - -@comparison = @equalitytest | @relationalcomparison; - -@equalitytest = @eqlexpr | @neqexpr; - -@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; - -@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; - -case @stmt.kind of - 0 = @badstmt -| 1 = @declstmt -| 2 = @emptystmt -| 3 = @labeledstmt -| 4 = @exprstmt -| 5 = @sendstmt -| 6 = @incstmt -| 7 = @decstmt -| 8 = @gostmt -| 9 = @deferstmt -| 10 = @returnstmt -| 11 = @breakstmt -| 12 = @continuestmt -| 13 = @gotostmt -| 14 = @fallthroughstmt -| 15 = @blockstmt -| 16 = @ifstmt -| 17 = @caseclause -| 18 = @exprswitchstmt -| 19 = @typeswitchstmt -| 20 = @commclause -| 21 = @selectstmt -| 22 = @forstmt -| 23 = @rangestmt -| 24 = @assignstmt -| 25 = @definestmt -| 26 = @addassignstmt -| 27 = @subassignstmt -| 28 = @mulassignstmt -| 29 = @quoassignstmt -| 30 = @remassignstmt -| 31 = @andassignstmt -| 32 = @orassignstmt -| 33 = @xorassignstmt -| 34 = @shlassignstmt -| 35 = @shrassignstmt -| 36 = @andnotassignstmt; - -@incdecstmt = @incstmt | @decstmt; - -@assignment = @simpleassignstmt | @compoundassignstmt; - -@simpleassignstmt = @assignstmt | @definestmt; - -@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt - | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; - -@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; - -@switchstmt = @exprswitchstmt | @typeswitchstmt; - -@loopstmt = @forstmt | @rangestmt; - -case @decl.kind of - 0 = @baddecl -| 1 = @importdecl -| 2 = @constdecl -| 3 = @typedecl -| 4 = @vardecl -| 5 = @funcdecl; - -@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; - -case @spec.kind of - 0 = @importspec -| 1 = @valuespec -| 2 = @typedefspec -| 3 = @aliasspec; - -@typespec = @typedefspec | @aliasspec; - -case @object.kind of - 0 = @pkgobject -| 1 = @decltypeobject -| 2 = @builtintypeobject -| 3 = @declconstobject -| 4 = @builtinconstobject -| 5 = @declvarobject -| 6 = @declfunctionobject -| 7 = @builtinfunctionobject -| 8 = @labelobject; - -@typeparamparentobject = @decltypeobject | @declfunctionobject; - -@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; - -@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; - -@typeobject = @decltypeobject | @builtintypeobject; - -@valueobject = @constobject | @varobject | @functionobject; - -@constobject = @declconstobject | @builtinconstobject; - -@varobject = @declvarobject; - -@functionobject = @declfunctionobject | @builtinfunctionobject; - -case @scope.kind of - 0 = @universescope -| 1 = @packagescope -| 2 = @localscope; - -case @type.kind of - 0 = @invalidtype -| 1 = @boolexprtype -| 2 = @inttype -| 3 = @int8type -| 4 = @int16type -| 5 = @int32type -| 6 = @int64type -| 7 = @uinttype -| 8 = @uint8type -| 9 = @uint16type -| 10 = @uint32type -| 11 = @uint64type -| 12 = @uintptrtype -| 13 = @float32type -| 14 = @float64type -| 15 = @complex64type -| 16 = @complex128type -| 17 = @stringexprtype -| 18 = @unsafepointertype -| 19 = @boolliteraltype -| 20 = @intliteraltype -| 21 = @runeliteraltype -| 22 = @floatliteraltype -| 23 = @complexliteraltype -| 24 = @stringliteraltype -| 25 = @nilliteraltype -| 26 = @typeparamtype -| 27 = @arraytype -| 28 = @slicetype -| 29 = @structtype -| 30 = @pointertype -| 31 = @interfacetype -| 32 = @tupletype -| 33 = @signaturetype -| 34 = @maptype -| 35 = @sendchantype -| 36 = @recvchantype -| 37 = @sendrcvchantype -| 38 = @namedtype -| 39 = @typesetliteraltype; - -@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; - -@booltype = @boolexprtype | @boolliteraltype; - -@numerictype = @integertype | @floattype | @complextype; - -@integertype = @signedintegertype | @unsignedintegertype; - -@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; - -@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; - -@floattype = @float32type | @float64type | @floatliteraltype; - -@complextype = @complex64type | @complex128type | @complexliteraltype; - -@stringtype = @stringexprtype | @stringliteraltype; - -@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype - | @stringliteraltype | @nilliteraltype; - -@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype - | @signaturetype | @namedtype | @typesetliteraltype; - -@containertype = @arraytype | @slicetype | @maptype | @chantype; - -@chantype = @sendchantype | @recvchantype | @sendrcvchantype; - -case @modexpr.kind of - 0 = @modcommentblock -| 1 = @modline -| 2 = @modlineblock -| 3 = @modlparen -| 4 = @modrparen; - -case @error.kind of - 0 = @unknownerror -| 1 = @listerror -| 2 = @parseerror -| 3 = @typeerror; - diff --git a/ql/lib/go.dbscheme.stats b/ql/lib/go.dbscheme.stats deleted file mode 100644 index b18e8556f..000000000 --- a/ql/lib/go.dbscheme.stats +++ /dev/null @@ -1,15183 +0,0 @@ - - - - @similarity - 0 - - - @duplication - 0 - - - @xmldtd - 0 - - - @xmlelement - 504 - - - @xmlattribute - 408 - - - @externalDataElement - 0 - - - @xmlnamespace - 0 - - - @xmlcomment - 30 - - - @xmlcharacters - 869 - - - @compilation - 1 - - - @diagnostic - 0 - - - @file - 529 - - - @folder - 210 - - - @comment_group - 12083 - - - @slashslashcomment - 24878 - - - @slashstarcomment - 846 - - - @ident - 237316 - - - @ellipsis - 141 - - - @intlit - 7683 - - - @floatlit - 27 - - - @charlit - 838 - - - @stringlit - 24892 - - - @funclit - 678 - - - @compositelit - 2704 - - - @parenexpr - 343 - - - @selectorexpr - 54353 - - - @indexexpr - 4581 - - - @sliceexpr - 836 - - - @typeassertexpr - 2127 - - - @callorconversionexpr - 32041 - - - @starexpr - 10360 - - - @keyvalueexpr - 5616 - - - @arraytypeexpr - 3465 - - - @structtypeexpr - 1207 - - - @functypeexpr - 6015 - - - @interfacetypeexpr - 509 - - - @maptypeexpr - 1013 - - - @minusexpr - 270 - - - @notexpr - 1190 - - - @complementexpr - 21 - - - @addressexpr - 1739 - - - @arrowexpr - 92 - - - @lorexpr - 612 - - - @landexpr - 1234 - - - @eqlexpr - 3244 - - - @neqexpr - 4103 - - - @lssexpr - 785 - - - @leqexpr - 248 - - - @gtrexpr - 619 - - - @geqexpr - 270 - - - @addexpr - 1272 - - - @subexpr - 557 - - - @orexpr - 146 - - - @xorexpr - 14 - - - @mulexpr - 207 - - - @quoexpr - 53 - - - @remexpr - 24 - - - @shlexpr - 164 - - - @shrexpr - 57 - - - @andexpr - 235 - - - @andnotexpr - 19 - - - @sendchantypeexpr - 7 - - - @recvchantypeexpr - 9 - - - @sendrcvchantypeexpr - 101 - - - @badexpr - 0 - - - @imaglit - 0 - - - @plusexpr - 0 - - - @derefexpr - 0 - - - @field - 19974 - - - @location_default - 539178 - - - @declstmt - 1454 - - - @labeledstmt - 49 - - - @exprstmt - 7605 - - - @sendstmt - 69 - - - @incstmt - 614 - - - @decstmt - 71 - - - @gostmt - 72 - - - @deferstmt - 358 - - - @returnstmt - 9225 - - - @breakstmt - 301 - - - @continuestmt - 606 - - - @gotostmt - 8 - - - @fallthroughstmt - 7 - - - @blockstmt - 19358 - - - @ifstmt - 9728 - - - @caseclause - 3476 - - - @exprswitchstmt - 378 - - - @typeswitchstmt - 400 - - - @commclause - 72 - - - @selectstmt - 35 - - - @forstmt - 654 - - - @rangestmt - 2135 - - - @assignstmt - 7478 - - - @definestmt - 9514 - - - @addassignstmt - 223 - - - @subassignstmt - 18 - - - @mulassignstmt - 5 - - - @quoassignstmt - 3 - - - @orassignstmt - 63 - - - @xorassignstmt - 3 - - - @shlassignstmt - 2 - - - @shrassignstmt - 3 - - - @andnotassignstmt - 3 - - - @badstmt - 0 - - - @emptystmt - 0 - - - @remassignstmt - 0 - - - @andassignstmt - 0 - - - @importdecl - 479 - - - @constdecl - 280 - - - @typedecl - 1349 - - - @vardecl - 1769 - - - @funcdecl - 4813 - - - @baddecl - 0 - - - @importspec - 3468 - - - @valuespec - 3056 - - - @typedefspec - 1349 - - - @aliasspec - 16 - - - @universescope - 1 - - - @packagescope - 346 - - - @localscope - 36428 - - - @pkgobject - 3468 - - - @decltypeobject - 3602 - - - @builtintypeobject - 20 - - - @declconstobject - 8857 - - - @builtinconstobject - 4 - - - @declvarobject - 51098 - - - @declfunctionobject - 17793 - - - @builtinfunctionobject - 18 - - - @labelobject - 49 - - - @invalidtype - 1 - - - @boolexprtype - 1 - - - @inttype - 1 - - - @int8type - 1 - - - @int16type - 1 - - - @int32type - 1 - - - @int64type - 1 - - - @uinttype - 1 - - - @uint8type - 1 - - - @uint16type - 1 - - - @uint32type - 1 - - - @uint64type - 1 - - - @uintptrtype - 1 - - - @float32type - 1 - - - @float64type - 1 - - - @complex64type - 1 - - - @complex128type - 1 - - - @stringexprtype - 1 - - - @unsafepointertype - 1 - - - @boolliteraltype - 1 - - - @intliteraltype - 1 - - - @runeliteraltype - 1 - - - @floatliteraltype - 1 - - - @stringliteraltype - 1 - - - @nilliteraltype - 1 - - - @arraytype - 293 - - - @slicetype - 637 - - - @structtype - 2409 - - - @pointertype - 1903 - - - @interfacetype - 247 - - - @tupletype - 559 - - - @signaturetype - 8010 - - - @maptype - 430 - - - @sendchantype - 13 - - - @recvchantype - 10 - - - @sendrcvchantype - 29 - - - @namedtype - 3567 - - - @complexliteraltype - 0 - - - @package - 346 - - - @modline - 6 - - - @modlineblock - 1 - - - @modlparen - 1 - - - @modrparen - 1 - - - @modcommentblock - 0 - - - @unknownerror - 0 - - - @listerror - 0 - - - @parseerror - 0 - - - @typeerror - 0 - - - - - duplicateCode - 0 - - - id - 0 - - - relativePath - 0 - - - equivClass - 0 - - - - - id - relativePath - - - 12 - - - 1 - 2 - 1 - - - - - - - id - equivClass - - - 12 - - - 1 - 2 - 1 - - - - - - - relativePath - id - - - 12 - - - - - - relativePath - equivClass - - - 12 - - - - - - equivClass - id - - - 12 - - - - - - equivClass - relativePath - - - 12 - - - - - - - - similarCode - 0 - - - id - 0 - - - relativePath - 0 - - - equivClass - 0 - - - - - id - relativePath - - - 12 - - - 1 - 2 - 1 - - - - - - - id - equivClass - - - 12 - - - 1 - 2 - 1 - - - - - - - relativePath - id - - - 12 - - - - - - relativePath - equivClass - - - 12 - - - - - - equivClass - id - - - 12 - - - - - - equivClass - relativePath - - - 12 - - - - - - - - tokens - 0 - - - id - 0 - - - offset - 0 - - - beginLine - 0 - - - beginColumn - 0 - - - endLine - 0 - - - endColumn - 0 - - - - - id - offset - - - 12 - - - - - - id - beginLine - - - 12 - - - - - - id - beginColumn - - - 12 - - - - - - id - endLine - - - 12 - - - - - - id - endColumn - - - 12 - - - - - - offset - id - - - 12 - - - - - - offset - beginLine - - - 12 - - - - - - offset - beginColumn - - - 12 - - - - - - offset - endLine - - - 12 - - - - - - offset - endColumn - - - 12 - - - - - - beginLine - id - - - 12 - - - - - - beginLine - offset - - - 12 - - - - - - beginLine - beginColumn - - - 12 - - - - - - beginLine - endLine - - - 12 - - - - - - beginLine - endColumn - - - 12 - - - - - - beginColumn - id - - - 12 - - - - - - beginColumn - offset - - - 12 - - - - - - beginColumn - beginLine - - - 12 - - - - - - beginColumn - endLine - - - 12 - - - - - - beginColumn - endColumn - - - 12 - - - - - - endLine - id - - - 12 - - - - - - endLine - offset - - - 12 - - - - - - endLine - beginLine - - - 12 - - - - - - endLine - beginColumn - - - 12 - - - - - - endLine - endColumn - - - 12 - - - - - - endColumn - id - - - 12 - - - - - - endColumn - offset - - - 12 - - - - - - endColumn - beginLine - - - 12 - - - - - - endColumn - beginColumn - - - 12 - - - - - - endColumn - endLine - - - 12 - - - - - - - - externalData - 0 - - - id - 0 - - - path - 0 - - - column - 0 - - - value - 0 - - - - - id - path - - - 12 - - - - - - id - column - - - 12 - - - - - - id - value - - - 12 - - - - - - path - id - - - 12 - - - - - - path - column - - - 12 - - - - - - path - value - - - 12 - - - - - - column - id - - - 12 - - - - - - column - path - - - 12 - - - - - - column - value - - - 12 - - - - - - value - id - - - 12 - - - - - - value - path - - - 12 - - - - - - value - column - - - 12 - - - - - - - - snapshotDate - 0 - - - snapshotDate - 0 - - - - - - sourceLocationPrefix - 1 - - - prefix - 1 - - - - - - xmlEncoding - 0 - - - id - 0 - - - encoding - 0 - - - - - id - encoding - - - 12 - - - 1 - 2 - 1 - - - - - - - encoding - id - - - 12 - - - - - - - - xmlDTDs - 0 - - - id - 0 - - - root - 0 - - - publicId - 0 - - - systemId - 0 - - - fileid - 0 - - - - - id - root - - - 12 - - - 1 - 2 - 1 - - - - - - - id - publicId - - - 12 - - - 1 - 2 - 1 - - - - - - - id - systemId - - - 12 - - - 1 - 2 - 1 - - - - - - - id - fileid - - - 12 - - - 1 - 2 - 1 - - - - - - - root - id - - - 12 - - - - - - root - publicId - - - 12 - - - - - - root - systemId - - - 12 - - - - - - root - fileid - - - 12 - - - - - - publicId - id - - - 12 - - - - - - publicId - root - - - 12 - - - - - - publicId - systemId - - - 12 - - - - - - publicId - fileid - - - 12 - - - - - - systemId - id - - - 12 - - - - - - systemId - root - - - 12 - - - - - - systemId - publicId - - - 12 - - - - - - systemId - fileid - - - 12 - - - - - - fileid - id - - - 12 - - - - - - fileid - root - - - 12 - - - - - - fileid - publicId - - - 12 - - - - - - fileid - systemId - - - 12 - - - - - - - - xmlElements - 504 - - - id - 504 - - - name - 38 - - - parentid - 199 - - - idx - 86 - - - fileid - 14 - - - - - id - name - - - 12 - - - 1 - 2 - 504 - - - - - - - id - parentid - - - 12 - - - 1 - 2 - 504 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 504 - - - - - - - id - fileid - - - 12 - - - 1 - 2 - 504 - - - - - - - name - id - - - 12 - - - 1 - 2 - 11 - - - 2 - 3 - 3 - - - 3 - 4 - 4 - - - 4 - 6 - 3 - - - 7 - 12 - 3 - - - 12 - 14 - 2 - - - 14 - 16 - 3 - - - 16 - 22 - 3 - - - 27 - 40 - 3 - - - 55 - 101 - 3 - - - - - - - name - parentid - - - 12 - - - 1 - 2 - 15 - - - 2 - 3 - 4 - - - 3 - 4 - 4 - - - 4 - 5 - 3 - - - 5 - 6 - 1 - - - 6 - 7 - 3 - - - 7 - 18 - 3 - - - 18 - 22 - 3 - - - 29 - 76 - 2 - - - - - - - name - idx - - - 12 - - - 1 - 2 - 12 - - - 2 - 3 - 4 - - - 3 - 4 - 5 - - - 4 - 5 - 1 - - - 5 - 6 - 3 - - - 6 - 9 - 3 - - - 10 - 11 - 2 - - - 13 - 15 - 3 - - - 15 - 17 - 3 - - - 17 - 41 - 2 - - - - - - - name - fileid - - - 12 - - - 1 - 2 - 18 - - - 2 - 3 - 7 - - - 3 - 4 - 3 - - - 4 - 5 - 1 - - - 5 - 6 - 4 - - - 7 - 11 - 3 - - - 11 - 14 - 2 - - - - - - - parentid - id - - - 12 - - - 1 - 2 - 116 - - - 2 - 3 - 37 - - - 3 - 4 - 22 - - - 4 - 8 - 15 - - - 8 - 61 - 9 - - - - - - - parentid - name - - - 12 - - - 1 - 2 - 160 - - - 2 - 3 - 23 - - - 3 - 8 - 16 - - - - - - - parentid - idx - - - 12 - - - 1 - 2 - 116 - - - 2 - 3 - 37 - - - 3 - 4 - 22 - - - 4 - 8 - 15 - - - 8 - 61 - 9 - - - - - - - parentid - fileid - - - 12 - - - 1 - 2 - 199 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 54 - - - 2 - 3 - 4 - - - 3 - 4 - 11 - - - 4 - 6 - 6 - - - 6 - 21 - 7 - - - 40 - 141 - 4 - - - - - - - idx - name - - - 12 - - - 1 - 2 - 54 - - - 2 - 3 - 5 - - - 3 - 4 - 11 - - - 4 - 5 - 6 - - - 5 - 12 - 7 - - - 14 - 26 - 3 - - - - - - - idx - parentid - - - 12 - - - 1 - 2 - 54 - - - 2 - 3 - 4 - - - 3 - 4 - 11 - - - 4 - 6 - 6 - - - 6 - 21 - 7 - - - 40 - 141 - 4 - - - - - - - idx - fileid - - - 12 - - - 1 - 2 - 54 - - - 2 - 3 - 4 - - - 3 - 4 - 11 - - - 4 - 5 - 8 - - - 5 - 13 - 7 - - - 13 - 15 - 2 - - - - - - - fileid - id - - - 12 - - - 2 - 3 - 1 - - - 7 - 8 - 1 - - - 8 - 9 - 2 - - - 10 - 11 - 1 - - - 16 - 17 - 1 - - - 18 - 19 - 1 - - - 20 - 21 - 1 - - - 21 - 22 - 2 - - - 58 - 59 - 1 - - - 100 - 101 - 1 - - - 107 - 108 - 1 - - - 108 - 109 - 1 - - - - - - - fileid - name - - - 12 - - - 2 - 3 - 1 - - - 3 - 4 - 2 - - - 4 - 5 - 1 - - - 5 - 6 - 2 - - - 6 - 7 - 2 - - - 8 - 9 - 2 - - - 11 - 12 - 1 - - - 16 - 17 - 1 - - - 17 - 18 - 1 - - - 19 - 20 - 1 - - - - - - - fileid - parentid - - - 12 - - - 2 - 3 - 2 - - - 4 - 5 - 1 - - - 6 - 7 - 2 - - - 7 - 8 - 3 - - - 9 - 10 - 1 - - - 10 - 11 - 1 - - - 20 - 21 - 1 - - - 23 - 24 - 1 - - - 47 - 48 - 1 - - - 49 - 50 - 1 - - - - - - - fileid - idx - - - 12 - - - 1 - 2 - 1 - - - 3 - 4 - 2 - - - 4 - 5 - 1 - - - 7 - 8 - 3 - - - 8 - 9 - 1 - - - 13 - 14 - 1 - - - 14 - 15 - 1 - - - 16 - 17 - 1 - - - 30 - 31 - 1 - - - 34 - 35 - 1 - - - 67 - 68 - 1 - - - - - - - - - xmlAttrs - 408 - - - id - 408 - - - elementid - 288 - - - name - 28 - - - value - 235 - - - idx - 6 - - - fileid - 14 - - - - - id - elementid - - - 12 - - - 1 - 2 - 408 - - - - - - - id - name - - - 12 - - - 1 - 2 - 408 - - - - - - - id - value - - - 12 - - - 1 - 2 - 408 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 408 - - - - - - - id - fileid - - - 12 - - - 1 - 2 - 408 - - - - - - - elementid - id - - - 12 - - - 1 - 2 - 195 - - - 2 - 3 - 69 - - - 3 - 4 - 23 - - - 6 - 7 - 1 - - - - - - - elementid - name - - - 12 - - - 1 - 2 - 195 - - - 2 - 3 - 69 - - - 3 - 4 - 23 - - - 6 - 7 - 1 - - - - - - - elementid - value - - - 12 - - - 1 - 2 - 195 - - - 2 - 3 - 70 - - - 3 - 4 - 22 - - - 4 - 5 - 1 - - - - - - - elementid - idx - - - 12 - - - 1 - 2 - 195 - - - 2 - 3 - 69 - - - 3 - 4 - 23 - - - 6 - 7 - 1 - - - - - - - elementid - fileid - - - 12 - - - 1 - 2 - 288 - - - - - - - name - id - - - 12 - - - 1 - 2 - 12 - - - 2 - 3 - 3 - - - 3 - 4 - 4 - - - 5 - 15 - 2 - - - 21 - 22 - 2 - - - 22 - 23 - 2 - - - 45 - 97 - 2 - - - 132 - 133 - 1 - - - - - - - name - elementid - - - 12 - - - 1 - 2 - 12 - - - 2 - 3 - 3 - - - 3 - 4 - 4 - - - 5 - 15 - 2 - - - 21 - 22 - 2 - - - 22 - 23 - 2 - - - 45 - 97 - 2 - - - 132 - 133 - 1 - - - - - - - name - value - - - 12 - - - 1 - 2 - 16 - - - 2 - 3 - 2 - - - 3 - 4 - 3 - - - 11 - 12 - 1 - - - 15 - 16 - 2 - - - 21 - 36 - 2 - - - 41 - 75 - 2 - - - - - - - name - idx - - - 12 - - - 1 - 2 - 19 - - - 2 - 3 - 5 - - - 3 - 4 - 4 - - - - - - - name - fileid - - - 12 - - - 1 - 2 - 18 - - - 2 - 3 - 2 - - - 3 - 4 - 2 - - - 4 - 6 - 2 - - - 10 - 11 - 3 - - - 13 - 14 - 1 - - - - - - - value - id - - - 12 - - - 1 - 2 - 171 - - - 2 - 3 - 32 - - - 3 - 5 - 18 - - - 5 - 13 - 14 - - - - - - - value - elementid - - - 12 - - - 1 - 2 - 174 - - - 2 - 3 - 29 - - - 3 - 5 - 18 - - - 5 - 13 - 14 - - - - - - - value - name - - - 12 - - - 1 - 2 - 230 - - - 2 - 4 - 5 - - - - - - - value - idx - - - 12 - - - 1 - 2 - 224 - - - 2 - 4 - 11 - - - - - - - value - fileid - - - 12 - - - 1 - 2 - 193 - - - 2 - 3 - 32 - - - 3 - 7 - 10 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 3 - - - 24 - 25 - 1 - - - 93 - 94 - 1 - - - 288 - 289 - 1 - - - - - - - idx - elementid - - - 12 - - - 1 - 2 - 3 - - - 24 - 25 - 1 - - - 93 - 94 - 1 - - - 288 - 289 - 1 - - - - - - - idx - name - - - 12 - - - 1 - 2 - 3 - - - 8 - 9 - 1 - - - 14 - 15 - 1 - - - 16 - 17 - 1 - - - - - - - idx - value - - - 12 - - - 1 - 2 - 3 - - - 23 - 24 - 1 - - - 64 - 65 - 1 - - - 157 - 158 - 1 - - - - - - - idx - fileid - - - 12 - - - 1 - 2 - 3 - - - 6 - 7 - 1 - - - 12 - 13 - 1 - - - 14 - 15 - 1 - - - - - - - fileid - id - - - 12 - - - 2 - 3 - 1 - - - 4 - 5 - 1 - - - 10 - 11 - 2 - - - 11 - 12 - 1 - - - 12 - 13 - 1 - - - 17 - 18 - 1 - - - 18 - 19 - 1 - - - 19 - 20 - 1 - - - 22 - 23 - 1 - - - 48 - 49 - 1 - - - 73 - 74 - 2 - - - 89 - 90 - 1 - - - - - - - fileid - elementid - - - 12 - - - 1 - 2 - 1 - - - 4 - 5 - 1 - - - 8 - 9 - 2 - - - 9 - 10 - 1 - - - 11 - 12 - 1 - - - 12 - 13 - 2 - - - 16 - 17 - 1 - - - 17 - 18 - 1 - - - 18 - 19 - 1 - - - 42 - 43 - 1 - - - 63 - 64 - 1 - - - 67 - 68 - 1 - - - - - - - fileid - name - - - 12 - - - 2 - 3 - 2 - - - 3 - 4 - 2 - - - 4 - 5 - 4 - - - 5 - 6 - 1 - - - 6 - 7 - 2 - - - 7 - 8 - 2 - - - 23 - 24 - 1 - - - - - - - fileid - value - - - 12 - - - 2 - 3 - 1 - - - 3 - 4 - 1 - - - 6 - 7 - 1 - - - 8 - 9 - 2 - - - 9 - 10 - 1 - - - 10 - 11 - 1 - - - 13 - 14 - 1 - - - 14 - 15 - 1 - - - 18 - 19 - 1 - - - 32 - 33 - 1 - - - 54 - 55 - 1 - - - 60 - 61 - 1 - - - 61 - 62 - 1 - - - - - - - fileid - idx - - - 12 - - - 1 - 2 - 2 - - - 2 - 3 - 6 - - - 3 - 4 - 5 - - - 6 - 7 - 1 - - - - - - - - - xmlNs - 0 - - - id - 0 - - - prefixName - 0 - - - URI - 0 - - - fileid - 0 - - - - - id - prefixName - - - 12 - - - - - - id - URI - - - 12 - - - - - - id - fileid - - - 12 - - - - - - prefixName - id - - - 12 - - - - - - prefixName - URI - - - 12 - - - - - - prefixName - fileid - - - 12 - - - - - - URI - id - - - 12 - - - - - - URI - prefixName - - - 12 - - - - - - URI - fileid - - - 12 - - - - - - fileid - id - - - 12 - - - - - - fileid - prefixName - - - 12 - - - - - - fileid - URI - - - 12 - - - - - - - - xmlHasNs - 0 - - - elementId - 0 - - - nsId - 0 - - - fileid - 0 - - - - - elementId - nsId - - - 12 - - - - - - elementId - fileid - - - 12 - - - - - - nsId - elementId - - - 12 - - - - - - nsId - fileid - - - 12 - - - - - - fileid - elementId - - - 12 - - - - - - fileid - nsId - - - 12 - - - - - - - - xmlComments - 30 - - - id - 30 - - - text - 18 - - - parentid - 20 - - - fileid - 10 - - - - - id - text - - - 12 - - - 1 - 2 - 30 - - - - - - - id - parentid - - - 12 - - - 1 - 2 - 30 - - - - - - - id - fileid - - - 12 - - - 1 - 2 - 30 - - - - - - - text - id - - - 12 - - - 1 - 2 - 13 - - - 2 - 3 - 3 - - - 4 - 5 - 1 - - - 7 - 8 - 1 - - - - - - - text - parentid - - - 12 - - - 1 - 2 - 14 - - - 2 - 3 - 2 - - - 4 - 5 - 1 - - - 7 - 8 - 1 - - - - - - - text - fileid - - - 12 - - - 1 - 2 - 15 - - - 2 - 3 - 2 - - - 7 - 8 - 1 - - - - - - - parentid - id - - - 12 - - - 1 - 2 - 15 - - - 2 - 3 - 3 - - - 4 - 5 - 1 - - - 5 - 6 - 1 - - - - - - - parentid - text - - - 12 - - - 1 - 2 - 15 - - - 2 - 3 - 3 - - - 3 - 4 - 1 - - - 5 - 6 - 1 - - - - - - - parentid - fileid - - - 12 - - - 1 - 2 - 20 - - - - - - - fileid - id - - - 12 - - - 1 - 2 - 6 - - - 2 - 3 - 1 - - - 4 - 5 - 1 - - - 8 - 9 - 1 - - - 10 - 11 - 1 - - - - - - - fileid - text - - - 12 - - - 1 - 2 - 6 - - - 2 - 3 - 1 - - - 4 - 5 - 1 - - - 5 - 6 - 1 - - - 9 - 10 - 1 - - - - - - - fileid - parentid - - - 12 - - - 1 - 2 - 7 - - - 3 - 4 - 1 - - - 5 - 6 - 2 - - - - - - - - - xmlChars - 869 - - - id - 869 - - - text - 427 - - - parentid - 432 - - - idx - 87 - - - isCDATA - 1 - - - fileid - 14 - - - - - id - text - - - 12 - - - 1 - 2 - 869 - - - - - - - id - parentid - - - 12 - - - 1 - 2 - 869 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 869 - - - - - - - id - isCDATA - - - 12 - - - 1 - 2 - 869 - - - - - - - id - fileid - - - 12 - - - 1 - 2 - 869 - - - - - - - text - id - - - 12 - - - 1 - 2 - 339 - - - 2 - 3 - 53 - - - 3 - 49 - 33 - - - 68 - 90 - 2 - - - - - - - text - parentid - - - 12 - - - 1 - 2 - 342 - - - 2 - 3 - 50 - - - 3 - 28 - 33 - - - 28 - 32 - 2 - - - - - - - text - idx - - - 12 - - - 1 - 2 - 400 - - - 2 - 58 - 27 - - - - - - - text - isCDATA - - - 12 - - - 1 - 2 - 427 - - - - - - - text - fileid - - - 12 - - - 1 - 2 - 380 - - - 2 - 4 - 36 - - - 4 - 11 - 11 - - - - - - - parentid - id - - - 12 - - - 1 - 2 - 302 - - - 2 - 3 - 53 - - - 3 - 4 - 28 - - - 4 - 7 - 34 - - - 7 - 60 - 15 - - - - - - - parentid - text - - - 12 - - - 1 - 2 - 314 - - - 2 - 3 - 67 - - - 3 - 5 - 37 - - - 5 - 26 - 14 - - - - - - - parentid - idx - - - 12 - - - 1 - 2 - 302 - - - 2 - 3 - 53 - - - 3 - 4 - 28 - - - 4 - 7 - 34 - - - 7 - 60 - 15 - - - - - - - parentid - isCDATA - - - 12 - - - 1 - 2 - 432 - - - - - - - parentid - fileid - - - 12 - - - 1 - 2 - 432 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 55 - - - 2 - 3 - 3 - - - 3 - 4 - 11 - - - 4 - 7 - 7 - - - 8 - 28 - 7 - - - 41 - 408 - 4 - - - - - - - idx - text - - - 12 - - - 1 - 2 - 55 - - - 2 - 3 - 3 - - - 3 - 4 - 12 - - - 4 - 7 - 7 - - - 7 - 28 - 7 - - - 44 - 251 - 3 - - - - - - - idx - parentid - - - 12 - - - 1 - 2 - 55 - - - 2 - 3 - 3 - - - 3 - 4 - 11 - - - 4 - 7 - 7 - - - 8 - 28 - 7 - - - 41 - 408 - 4 - - - - - - - idx - isCDATA - - - 12 - - - 1 - 2 - 87 - - - - - - - idx - fileid - - - 12 - - - 1 - 2 - 55 - - - 2 - 3 - 3 - - - 3 - 4 - 12 - - - 4 - 5 - 7 - - - 5 - 12 - 7 - - - 13 - 15 - 3 - - - - - - - isCDATA - id - - - 12 - - - 869 - 870 - 1 - - - - - - - isCDATA - text - - - 12 - - - 427 - 428 - 1 - - - - - - - isCDATA - parentid - - - 12 - - - 432 - 433 - 1 - - - - - - - isCDATA - idx - - - 12 - - - 87 - 88 - 1 - - - - - - - isCDATA - fileid - - - 12 - - - 14 - 15 - 1 - - - - - - - fileid - id - - - 12 - - - 5 - 6 - 1 - - - 13 - 14 - 1 - - - 14 - 15 - 2 - - - 17 - 18 - 1 - - - 28 - 29 - 1 - - - 30 - 31 - 1 - - - 34 - 35 - 1 - - - 35 - 36 - 1 - - - 36 - 37 - 1 - - - 80 - 81 - 1 - - - 177 - 178 - 1 - - - 191 - 192 - 1 - - - 195 - 196 - 1 - - - - - - - fileid - text - - - 12 - - - 3 - 4 - 1 - - - 7 - 8 - 2 - - - 9 - 10 - 1 - - - 13 - 14 - 1 - - - 15 - 16 - 1 - - - 18 - 19 - 1 - - - 24 - 25 - 1 - - - 25 - 26 - 1 - - - 26 - 27 - 1 - - - 49 - 50 - 1 - - - 100 - 101 - 1 - - - 105 - 106 - 1 - - - 118 - 119 - 1 - - - - - - - fileid - parentid - - - 12 - - - 3 - 4 - 1 - - - 7 - 8 - 1 - - - 9 - 10 - 2 - - - 10 - 11 - 1 - - - 14 - 15 - 1 - - - 15 - 16 - 2 - - - 18 - 19 - 1 - - - 20 - 21 - 1 - - - 33 - 34 - 1 - - - 88 - 89 - 1 - - - 95 - 96 - 1 - - - 96 - 97 - 1 - - - - - - - fileid - idx - - - 12 - - - 2 - 3 - 1 - - - 4 - 5 - 2 - - - 5 - 6 - 1 - - - 7 - 8 - 3 - - - 9 - 10 - 1 - - - 13 - 14 - 1 - - - 15 - 16 - 2 - - - 32 - 33 - 1 - - - 35 - 36 - 1 - - - 65 - 66 - 1 - - - - - - - fileid - isCDATA - - - 12 - - - 1 - 2 - 14 - - - - - - - - - xmllocations - 1825 - - - xmlElement - 1825 - - - location - 1825 - - - - - xmlElement - location - - - 12 - - - 1 - 2 - 1825 - - - - - - - location - xmlElement - - - 12 - - - 1 - 2 - 1825 - - - - - - - - - compilations - 1 - - - id - 1 - - - cwd - 1 - - - - - id - cwd - - - 12 - - - 1 - 2 - 1 - - - - - - - cwd - id - - - 12 - - - 1 - 2 - 1 - - - - - - - - - compilation_args - 3 - - - id - 1 - - - num - 3 - - - arg - 3 - - - - - id - num - - - 12 - - - 3 - 4 - 1 - - - - - - - id - arg - - - 12 - - - 3 - 4 - 1 - - - - - - - num - id - - - 12 - - - 1 - 2 - 3 - - - - - - - num - arg - - - 12 - - - 1 - 2 - 3 - - - - - - - arg - id - - - 12 - - - 1 - 2 - 3 - - - - - - - arg - num - - - 12 - - - 1 - 2 - 3 - - - - - - - - - compilation_time - 0 - - - id - 0 - - - num - 0 - - - kind - 0 - - - secs - 0 - - - - - id - num - - - 12 - - - - - - id - kind - - - 12 - - - - - - id - secs - - - 12 - - - - - - num - id - - - 12 - - - - - - num - kind - - - 12 - - - - - - num - secs - - - 12 - - - - - - kind - id - - - 12 - - - - - - kind - num - - - 12 - - - - - - kind - secs - - - 12 - - - - - - secs - id - - - 12 - - - - - - secs - num - - - 12 - - - - - - secs - kind - - - 12 - - - - - - - - diagnostic_for - 0 - - - diagnostic - 0 - - - compilation - 0 - - - file_number - 0 - - - file_number_diagnostic_number - 0 - - - - - diagnostic - compilation - - - 12 - - - 1 - 2 - 1 - - - - - - - diagnostic - file_number - - - 12 - - - 1 - 2 - 1 - - - - - - - diagnostic - file_number_diagnostic_number - - - 12 - - - 1 - 2 - 1 - - - - - - - compilation - diagnostic - - - 12 - - - - - - compilation - file_number - - - 12 - - - - - - compilation - file_number_diagnostic_number - - - 12 - - - - - - file_number - diagnostic - - - 12 - - - - - - file_number - compilation - - - 12 - - - - - - file_number - file_number_diagnostic_number - - - 12 - - - - - - file_number_diagnostic_number - diagnostic - - - 12 - - - - - - file_number_diagnostic_number - compilation - - - 12 - - - - - - file_number_diagnostic_number - file_number - - - 12 - - - - - - - - compilation_finished - 1 - - - id - 1 - - - cpu_seconds - 1 - - - elapsed_seconds - 1 - - - - - id - cpu_seconds - - - 12 - - - 1 - 2 - 1 - - - - - - - id - elapsed_seconds - - - 12 - - - 1 - 2 - 1 - - - - - - - cpu_seconds - id - - - 12 - - - 1 - 2 - 1 - - - - - - - cpu_seconds - elapsed_seconds - - - 12 - - - 1 - 2 - 1 - - - - - - - elapsed_seconds - id - - - 12 - - - 1 - 2 - 1 - - - - - - - elapsed_seconds - cpu_seconds - - - 12 - - - 1 - 2 - 1 - - - - - - - - - compilation_compiling_files - 515 - - - id - 1 - - - num - 515 - - - file - 515 - - - - - id - num - - - 12 - - - 515 - 516 - 1 - - - - - - - id - file - - - 12 - - - 515 - 516 - 1 - - - - - - - num - id - - - 12 - - - 1 - 2 - 515 - - - - - - - num - file - - - 12 - - - 1 - 2 - 515 - - - - - - - file - id - - - 12 - - - 1 - 2 - 515 - - - - - - - file - num - - - 12 - - - 1 - 2 - 515 - - - - - - - - - diagnostics - 0 - - - id - 0 - - - severity - 0 - - - error_tag - 0 - - - error_message - 0 - - - full_error_message - 0 - - - location - 0 - - - - - id - severity - - - 12 - - - 1 - 2 - 1 - - - - - - - id - error_tag - - - 12 - - - 1 - 2 - 1 - - - - - - - id - error_message - - - 12 - - - 1 - 2 - 1 - - - - - - - id - full_error_message - - - 12 - - - 1 - 2 - 1 - - - - - - - id - location - - - 12 - - - 1 - 2 - 1 - - - - - - - severity - id - - - 12 - - - - - - severity - error_tag - - - 12 - - - - - - severity - error_message - - - 12 - - - - - - severity - full_error_message - - - 12 - - - - - - severity - location - - - 12 - - - - - - error_tag - id - - - 12 - - - - - - error_tag - severity - - - 12 - - - - - - error_tag - error_message - - - 12 - - - - - - error_tag - full_error_message - - - 12 - - - - - - error_tag - location - - - 12 - - - - - - error_message - id - - - 12 - - - - - - error_message - severity - - - 12 - - - - - - error_message - error_tag - - - 12 - - - - - - error_message - full_error_message - - - 12 - - - - - - error_message - location - - - 12 - - - - - - full_error_message - id - - - 12 - - - - - - full_error_message - severity - - - 12 - - - - - - full_error_message - error_tag - - - 12 - - - - - - full_error_message - error_message - - - 12 - - - - - - full_error_message - location - - - 12 - - - - - - location - id - - - 12 - - - - - - location - severity - - - 12 - - - - - - location - error_tag - - - 12 - - - - - - location - error_message - - - 12 - - - - - - location - full_error_message - - - 12 - - - - - - - - locations_default - 539178 - - - id - 539178 - - - file - 529 - - - beginLine - 10312 - - - beginColumn - 211 - - - endLine - 10378 - - - endColumn - 274 - - - - - id - file - - - 12 - - - 1 - 2 - 539178 - - - - - - - id - beginLine - - - 12 - - - 1 - 2 - 539178 - - - - - - - id - beginColumn - - - 12 - - - 1 - 2 - 539178 - - - - - - - id - endLine - - - 12 - - - 1 - 2 - 539178 - - - - - - - id - endColumn - - - 12 - - - 1 - 2 - 539178 - - - - - - - file - id - - - 12 - - - 4 - 39 - 41 - - - 39 - 120 - 40 - - - 120 - 208 - 40 - - - 210 - 290 - 40 - - - 291 - 372 - 40 - - - 372 - 453 - 41 - - - 456 - 563 - 40 - - - 565 - 769 - 40 - - - 774 - 1007 - 40 - - - 1012 - 1339 - 42 - - - 1347 - 1700 - 40 - - - 1701 - 2804 - 40 - - - 2873 - 6918 - 40 - - - 8171 - 11207 - 5 - - - - - - - file - beginLine - - - 12 - - - 3 - 15 - 44 - - - 15 - 27 - 42 - - - 27 - 46 - 40 - - - 46 - 63 - 43 - - - 63 - 78 - 40 - - - 78 - 94 - 41 - - - 95 - 120 - 40 - - - 120 - 152 - 41 - - - 152 - 188 - 40 - - - 189 - 247 - 41 - - - 249 - 325 - 40 - - - 336 - 544 - 40 - - - 554 - 10233 - 37 - - - - - - - file - beginColumn - - - 12 - - - 3 - 16 - 40 - - - 16 - 34 - 40 - - - 34 - 44 - 40 - - - 45 - 51 - 44 - - - 51 - 58 - 41 - - - 58 - 63 - 47 - - - 63 - 68 - 44 - - - 68 - 73 - 43 - - - 73 - 80 - 47 - - - 80 - 86 - 43 - - - 86 - 98 - 42 - - - 98 - 115 - 42 - - - 115 - 157 - 16 - - - - - - - file - endLine - - - 12 - - - 3 - 16 - 41 - - - 16 - 31 - 40 - - - 31 - 52 - 40 - - - 52 - 73 - 43 - - - 73 - 92 - 42 - - - 92 - 111 - 40 - - - 111 - 139 - 40 - - - 139 - 180 - 40 - - - 180 - 219 - 40 - - - 223 - 293 - 40 - - - 294 - 370 - 40 - - - 373 - 616 - 40 - - - 617 - 1835 - 40 - - - 2166 - 10377 - 3 - - - - - - - file - endColumn - - - 12 - - - 4 - 21 - 42 - - - 22 - 45 - 41 - - - 46 - 59 - 43 - - - 59 - 65 - 40 - - - 65 - 71 - 42 - - - 71 - 76 - 47 - - - 76 - 81 - 40 - - - 81 - 85 - 48 - - - 85 - 91 - 41 - - - 91 - 97 - 42 - - - 97 - 105 - 41 - - - 105 - 119 - 42 - - - 119 - 166 - 20 - - - - - - - beginLine - id - - - 12 - - - 1 - 2 - 6869 - - - 2 - 7 - 843 - - - 7 - 20 - 796 - - - 20 - 80 - 780 - - - 80 - 698 - 774 - - - 699 - 1775 - 250 - - - - - - - beginLine - file - - - 12 - - - 1 - 2 - 6947 - - - 2 - 3 - 868 - - - 3 - 6 - 899 - - - 6 - 24 - 774 - - - 24 - 304 - 774 - - - 305 - 530 - 50 - - - - - - - beginLine - beginColumn - - - 12 - - - 1 - 2 - 6894 - - - 2 - 5 - 789 - - - 5 - 11 - 780 - - - 11 - 30 - 796 - - - 30 - 72 - 779 - - - 72 - 115 - 274 - - - - - - - beginLine - endLine - - - 12 - - - 1 - 2 - 7630 - - - 2 - 3 - 1017 - - - 3 - 6 - 779 - - - 6 - 21 - 792 - - - 21 - 315 - 94 - - - - - - - beginLine - endColumn - - - 12 - - - 1 - 2 - 6871 - - - 2 - 5 - 793 - - - 5 - 12 - 789 - - - 12 - 33 - 778 - - - 33 - 81 - 781 - - - 81 - 127 - 300 - - - - - - - beginColumn - id - - - 12 - - - 1 - 2 - 29 - - - 2 - 4 - 19 - - - 4 - 9 - 16 - - - 10 - 22 - 16 - - - 22 - 62 - 16 - - - 62 - 141 - 16 - - - 144 - 330 - 16 - - - 330 - 759 - 16 - - - 781 - 1804 - 16 - - - 1846 - 3757 - 16 - - - 4042 - 8613 - 16 - - - 8764 - 22092 - 16 - - - 28067 - 55590 - 3 - - - - - - - beginColumn - file - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 14 - - - 4 - 10 - 18 - - - 10 - 22 - 17 - - - 22 - 48 - 17 - - - 49 - 96 - 17 - - - 96 - 180 - 16 - - - 180 - 292 - 16 - - - 297 - 382 - 16 - - - 383 - 430 - 16 - - - 431 - 463 - 16 - - - 463 - 530 - 12 - - - - - - - beginColumn - beginLine - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 13 - - - 4 - 9 - 16 - - - 9 - 20 - 16 - - - 20 - 53 - 16 - - - 55 - 103 - 16 - - - 107 - 209 - 16 - - - 221 - 412 - 16 - - - 422 - 682 - 16 - - - 706 - 975 - 16 - - - 977 - 1410 - 16 - - - 1417 - 1983 - 16 - - - 2809 - 10184 - 2 - - - - - - - beginColumn - endLine - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 13 - - - 4 - 9 - 16 - - - 9 - 21 - 19 - - - 24 - 55 - 16 - - - 58 - 128 - 16 - - - 131 - 226 - 16 - - - 231 - 453 - 16 - - - 480 - 769 - 16 - - - 783 - 1037 - 16 - - - 1057 - 1521 - 16 - - - 1526 - 10180 - 15 - - - - - - - beginColumn - endColumn - - - 12 - - - 1 - 2 - 31 - - - 2 - 3 - 16 - - - 3 - 6 - 17 - - - 6 - 12 - 17 - - - 12 - 21 - 16 - - - 21 - 28 - 16 - - - 28 - 35 - 17 - - - 35 - 45 - 16 - - - 46 - 56 - 16 - - - 56 - 77 - 17 - - - 77 - 101 - 17 - - - 101 - 201 - 15 - - - - - - - endLine - id - - - 12 - - - 1 - 2 - 6731 - - - 2 - 6 - 903 - - - 6 - 16 - 817 - - - 16 - 58 - 785 - - - 58 - 457 - 779 - - - 458 - 1734 - 363 - - - - - - - endLine - file - - - 12 - - - 1 - 2 - 6847 - - - 2 - 3 - 810 - - - 3 - 5 - 787 - - - 5 - 14 - 802 - - - 14 - 104 - 780 - - - 105 - 530 - 352 - - - - - - - endLine - beginLine - - - 12 - - - 1 - 2 - 7766 - - - 2 - 3 - 956 - - - 3 - 7 - 860 - - - 7 - 27 - 785 - - - 27 - 31 - 11 - - - - - - - endLine - beginColumn - - - 12 - - - 1 - 2 - 6749 - - - 2 - 4 - 616 - - - 4 - 8 - 878 - - - 8 - 21 - 799 - - - 21 - 53 - 779 - - - 53 - 113 - 557 - - - - - - - endLine - endColumn - - - 12 - - - 1 - 2 - 6792 - - - 2 - 5 - 936 - - - 5 - 12 - 801 - - - 12 - 34 - 791 - - - 34 - 83 - 787 - - - 83 - 127 - 271 - - - - - - - endColumn - id - - - 12 - - - 1 - 2 - 33 - - - 2 - 3 - 60 - - - 3 - 9 - 23 - - - 9 - 40 - 21 - - - 43 - 111 - 21 - - - 121 - 347 - 21 - - - 369 - 1229 - 21 - - - 1267 - 3311 - 21 - - - 3642 - 7560 - 21 - - - 7682 - 12716 - 21 - - - 12740 - 20483 - 11 - - - - - - - endColumn - file - - - 12 - - - 1 - 2 - 94 - - - 2 - 6 - 19 - - - 6 - 16 - 21 - - - 16 - 45 - 21 - - - 45 - 110 - 21 - - - 123 - 281 - 21 - - - 290 - 393 - 21 - - - 395 - 445 - 21 - - - 446 - 468 - 21 - - - 470 - 530 - 14 - - - - - - - endColumn - beginLine - - - 12 - - - 1 - 2 - 94 - - - 2 - 6 - 19 - - - 6 - 21 - 21 - - - 21 - 52 - 21 - - - 54 - 154 - 21 - - - 157 - 449 - 21 - - - 455 - 808 - 21 - - - 814 - 1132 - 21 - - - 1145 - 1769 - 21 - - - 1792 - 2365 - 14 - - - - - - - endColumn - beginColumn - - - 12 - - - 1 - 2 - 39 - - - 2 - 3 - 56 - - - 3 - 7 - 23 - - - 7 - 18 - 21 - - - 18 - 27 - 24 - - - 27 - 37 - 22 - - - 37 - 49 - 23 - - - 49 - 63 - 22 - - - 63 - 74 - 20 - - - 74 - 102 - 21 - - - 103 - 172 - 3 - - - - - - - endColumn - endLine - - - 12 - - - 1 - 2 - 94 - - - 2 - 6 - 19 - - - 6 - 21 - 21 - - - 21 - 52 - 21 - - - 53 - 153 - 21 - - - 156 - 444 - 21 - - - 446 - 789 - 21 - - - 806 - 1121 - 21 - - - 1138 - 1726 - 21 - - - 1787 - 2357 - 14 - - - - - - - - - numlines - 514 - - - element_id - 514 - - - num_lines - 309 - - - num_code - 350 - - - num_comment - 150 - - - - - element_id - num_lines - - - 12 - - - 1 - 2 - 514 - - - - - - - element_id - num_code - - - 12 - - - 1 - 2 - 514 - - - - - - - element_id - num_comment - - - 12 - - - 1 - 2 - 514 - - - - - - - num_lines - element_id - - - 12 - - - 1 - 2 - 183 - - - 2 - 3 - 74 - - - 3 - 4 - 32 - - - 4 - 7 - 20 - - - - - - - num_lines - num_code - - - 12 - - - 1 - 2 - 187 - - - 2 - 3 - 82 - - - 3 - 4 - 23 - - - 4 - 7 - 17 - - - - - - - num_lines - num_comment - - - 12 - - - 1 - 2 - 188 - - - 2 - 3 - 79 - - - 3 - 4 - 28 - - - 4 - 7 - 14 - - - - - - - num_code - element_id - - - 12 - - - 1 - 2 - 252 - - - 2 - 3 - 65 - - - 3 - 5 - 28 - - - 6 - 18 - 5 - - - - - - - num_code - num_lines - - - 12 - - - 1 - 2 - 256 - - - 2 - 3 - 67 - - - 3 - 18 - 27 - - - - - - - num_code - num_comment - - - 12 - - - 1 - 2 - 259 - - - 2 - 3 - 63 - - - 3 - 7 - 27 - - - 17 - 18 - 1 - - - - - - - num_comment - element_id - - - 12 - - - 1 - 2 - 65 - - - 2 - 3 - 27 - - - 3 - 4 - 17 - - - 4 - 5 - 7 - - - 5 - 6 - 10 - - - 6 - 10 - 12 - - - 10 - 31 - 12 - - - - - - - num_comment - num_lines - - - 12 - - - 1 - 2 - 65 - - - 2 - 3 - 27 - - - 3 - 4 - 18 - - - 4 - 5 - 7 - - - 5 - 6 - 10 - - - 6 - 10 - 13 - - - 10 - 25 - 10 - - - - - - - num_comment - num_code - - - 12 - - - 1 - 2 - 66 - - - 2 - 3 - 27 - - - 3 - 4 - 17 - - - 4 - 5 - 6 - - - 5 - 6 - 10 - - - 6 - 9 - 12 - - - 9 - 24 - 12 - - - - - - - - - files - 529 - - - id - 529 - - - name - 529 - - - - - id - name - - - 12 - - - 1 - 2 - 529 - - - - - - - name - id - - - 12 - - - 1 - 2 - 529 - - - - - - - - - folders - 210 - - - id - 210 - - - name - 210 - - - - - id - name - - - 12 - - - 1 - 2 - 210 - - - - - - - name - id - - - 12 - - - 1 - 2 - 210 - - - - - - - - - containerparent - 738 - - - parent - 210 - - - child - 738 - - - - - parent - child - - - 12 - - - 1 - 2 - 115 - - - 2 - 3 - 32 - - - 3 - 4 - 12 - - - 4 - 6 - 19 - - - 6 - 12 - 16 - - - 13 - 38 - 16 - - - - - - - child - parent - - - 12 - - - 1 - 2 - 738 - - - - - - - - - has_location - 599339 - - - locatable - 599339 - - - location - 537353 - - - - - locatable - location - - - 12 - - - 1 - 2 - 599339 - - - - - - - location - locatable - - - 12 - - - 1 - 2 - 475682 - - - 2 - 3 - 61627 - - - 3 - 75 - 44 - - - - - - - - - comment_groups - 12083 - - - id - 12083 - - - parent - 509 - - - idx - 720 - - - - - id - parent - - - 12 - - - 1 - 2 - 12083 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 12083 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 44 - - - 2 - 3 - 45 - - - 3 - 4 - 32 - - - 4 - 5 - 27 - - - 5 - 7 - 38 - - - 7 - 10 - 47 - - - 10 - 13 - 34 - - - 13 - 17 - 47 - - - 17 - 23 - 40 - - - 23 - 29 - 40 - - - 29 - 38 - 39 - - - 38 - 70 - 39 - - - 70 - 721 - 37 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 44 - - - 2 - 3 - 45 - - - 3 - 4 - 32 - - - 4 - 5 - 27 - - - 5 - 7 - 38 - - - 7 - 10 - 47 - - - 10 - 13 - 34 - - - 13 - 17 - 47 - - - 17 - 23 - 40 - - - 23 - 29 - 40 - - - 29 - 38 - 39 - - - 38 - 70 - 39 - - - 70 - 721 - 37 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 429 - - - 2 - 3 - 53 - - - 3 - 8 - 66 - - - 8 - 16 - 57 - - - 16 - 44 - 54 - - - 44 - 311 - 54 - - - 323 - 510 - 7 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 429 - - - 2 - 3 - 53 - - - 3 - 8 - 66 - - - 8 - 16 - 57 - - - 16 - 44 - 54 - - - 44 - 311 - 54 - - - 323 - 510 - 7 - - - - - - - - - comments - 25724 - - - id - 25724 - - - kind - 2 - - - parent - 12083 - - - idx - 156 - - - text - 20683 - - - - - id - kind - - - 12 - - - 1 - 2 - 25724 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 25724 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 25724 - - - - - - - id - text - - - 12 - - - 1 - 2 - 25724 - - - - - - - kind - id - - - 12 - - - 846 - 847 - 1 - - - 24878 - 24879 - 1 - - - - - - - kind - parent - - - 12 - - - 846 - 847 - 1 - - - 11239 - 11240 - 1 - - - - - - - kind - idx - - - 12 - - - 2 - 3 - 1 - - - 156 - 157 - 1 - - - - - - - kind - text - - - 12 - - - 690 - 691 - 1 - - - 19993 - 19994 - 1 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 7828 - - - 2 - 3 - 1787 - - - 3 - 4 - 1289 - - - 4 - 11 - 937 - - - 11 - 157 - 242 - - - - - - - parent - kind - - - 12 - - - 1 - 2 - 12081 - - - 2 - 3 - 2 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 7828 - - - 2 - 3 - 1787 - - - 3 - 4 - 1289 - - - 4 - 11 - 937 - - - 11 - 157 - 242 - - - - - - - parent - text - - - 12 - - - 1 - 2 - 7828 - - - 2 - 3 - 1817 - - - 3 - 4 - 1275 - - - 4 - 10 - 937 - - - 10 - 131 - 226 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 13 - - - 4 - 5 - 16 - - - 5 - 7 - 12 - - - 7 - 9 - 12 - - - 9 - 11 - 6 - - - 12 - 14 - 13 - - - 14 - 24 - 13 - - - 26 - 52 - 12 - - - 59 - 218 - 12 - - - 242 - 12084 - 11 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 154 - - - 2 - 3 - 2 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 13 - - - 4 - 5 - 16 - - - 5 - 7 - 12 - - - 7 - 9 - 12 - - - 9 - 11 - 6 - - - 12 - 14 - 13 - - - 14 - 24 - 13 - - - 26 - 52 - 12 - - - 59 - 218 - 12 - - - 242 - 12084 - 11 - - - - - - - idx - text - - - 12 - - - 1 - 2 - 36 - - - 2 - 4 - 14 - - - 4 - 5 - 18 - - - 5 - 7 - 14 - - - 7 - 9 - 11 - - - 9 - 12 - 14 - - - 12 - 17 - 12 - - - 19 - 36 - 13 - - - 39 - 128 - 12 - - - 165 - 10500 - 12 - - - - - - - text - id - - - 12 - - - 1 - 2 - 19550 - - - 2 - 1935 - 1133 - - - - - - - text - kind - - - 12 - - - 1 - 2 - 20683 - - - - - - - text - parent - - - 12 - - - 1 - 2 - 19575 - - - 2 - 828 - 1108 - - - - - - - text - idx - - - 12 - - - 1 - 2 - 20523 - - - 2 - 107 - 160 - - - - - - - - - doc_comments - 4330 - - - node - 4330 - - - comment - 4330 - - - - - node - comment - - - 12 - - - 1 - 2 - 4330 - - - - - - - comment - node - - - 12 - - - 1 - 2 - 4330 - - - - - - - - - exprs - 414037 - - - id - 414037 - - - kind - 48 - - - parent - 219324 - - - idx - 5163 - - - - - id - kind - - - 12 - - - 1 - 2 - 414037 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 414037 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 414037 - - - - - - - kind - id - - - 12 - - - 7 - 20 - 4 - - - 21 - 54 - 4 - - - 57 - 142 - 4 - - - 146 - 236 - 4 - - - 248 - 344 - 4 - - - 509 - 620 - 4 - - - 678 - 839 - 4 - - - 1013 - 1235 - 4 - - - 1272 - 2705 - 4 - - - 3244 - 4582 - 4 - - - 5616 - 10361 - 4 - - - 24892 - 237317 - 4 - - - - - - - kind - parent - - - 12 - - - 7 - 20 - 4 - - - 21 - 54 - 4 - - - 57 - 142 - 4 - - - 144 - 207 - 4 - - - 233 - 324 - 4 - - - 509 - 613 - 4 - - - 676 - 790 - 4 - - - 1013 - 1226 - 4 - - - 1239 - 1949 - 4 - - - 2582 - 3985 - 4 - - - 4252 - 10115 - 4 - - - 14086 - 154744 - 4 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 3 - - - 2 - 3 - 8 - - - 3 - 4 - 10 - - - 4 - 5 - 6 - - - 5 - 7 - 4 - - - 7 - 8 - 4 - - - 8 - 14 - 3 - - - 15 - 22 - 4 - - - 23 - 34 - 4 - - - 1057 - 5164 - 2 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 65804 - - - 2 - 3 - 133334 - - - 3 - 5 - 17889 - - - 5 - 5144 - 2297 - - - - - - - parent - kind - - - 12 - - - 1 - 2 - 137263 - - - 2 - 3 - 75813 - - - 3 - 7 - 6248 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 65804 - - - 2 - 3 - 133334 - - - 3 - 5 - 17889 - - - 5 - 5144 - 2297 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 3910 - - - 2 - 3 - 183 - - - 3 - 4 - 782 - - - 4 - 180006 - 288 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 4087 - - - 2 - 3 - 1033 - - - 3 - 48 - 43 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 3910 - - - 2 - 3 - 183 - - - 3 - 4 - 782 - - - 4 - 180006 - 288 - - - - - - - - - literals - 270756 - - - expr - 270756 - - - value - 25795 - - - raw - 27594 - - - - - expr - value - - - 12 - - - 1 - 2 - 270756 - - - - - - - expr - raw - - - 12 - - - 1 - 2 - 270756 - - - - - - - value - expr - - - 12 - - - 1 - 2 - 14635 - - - 2 - 3 - 3291 - - - 3 - 4 - 1783 - - - 4 - 6 - 1931 - - - 6 - 12 - 1968 - - - 12 - 139 - 1935 - - - 139 - 6840 - 252 - - - - - - - value - raw - - - 12 - - - 1 - 2 - 24051 - - - 2 - 5 - 1744 - - - - - - - raw - expr - - - 12 - - - 1 - 2 - 16038 - - - 2 - 3 - 3521 - - - 3 - 4 - 1869 - - - 4 - 7 - 2553 - - - 7 - 18 - 2129 - - - 18 - 6833 - 1484 - - - - - - - raw - value - - - 12 - - - 1 - 2 - 27594 - - - - - - - - - constvalues - 43931 - - - expr - 43931 - - - value - 16896 - - - exact - 16897 - - - - - expr - value - - - 12 - - - 1 - 2 - 43931 - - - - - - - expr - exact - - - 12 - - - 1 - 2 - 43931 - - - - - - - value - expr - - - 12 - - - 1 - 2 - 14932 - - - 2 - 4 - 1388 - - - 4 - 6766 - 576 - - - - - - - value - exact - - - 12 - - - 1 - 2 - 16895 - - - 2 - 3 - 1 - - - - - - - exact - expr - - - 12 - - - 1 - 2 - 14933 - - - 2 - 4 - 1388 - - - 4 - 6766 - 576 - - - - - - - exact - value - - - 12 - - - 1 - 2 - 16897 - - - - - - - - - fields - 19974 - - - id - 19974 - - - parent - 9600 - - - idx - 57 - - - - - id - parent - - - 12 - - - 1 - 2 - 19974 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 19974 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 4790 - - - 2 - 3 - 2214 - - - 3 - 4 - 1363 - - - 4 - 5 - 653 - - - 5 - 53 - 580 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 4790 - - - 2 - 3 - 2214 - - - 3 - 4 - 1363 - - - 4 - 5 - 653 - - - 5 - 53 - 580 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 14 - - - 2 - 3 - 8 - - - 3 - 6 - 5 - - - 6 - 11 - 5 - - - 12 - 18 - 5 - - - 19 - 31 - 5 - - - 46 - 105 - 5 - - - 115 - 633 - 5 - - - 914 - 7063 - 5 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 14 - - - 2 - 3 - 8 - - - 3 - 6 - 5 - - - 6 - 11 - 5 - - - 12 - 18 - 5 - - - 19 - 31 - 5 - - - 46 - 105 - 5 - - - 115 - 633 - 5 - - - 914 - 7063 - 5 - - - - - - - - - stmts - 73990 - - - id - 73990 - - - kind - 33 - - - parent - 41543 - - - idx - 81 - - - - - id - kind - - - 12 - - - 1 - 2 - 73990 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 73990 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 73990 - - - - - - - kind - id - - - 12 - - - 2 - 3 - 1 - - - 3 - 4 - 4 - - - 5 - 9 - 3 - - - 18 - 50 - 3 - - - 63 - 72 - 3 - - - 72 - 224 - 3 - - - 301 - 379 - 3 - - - 400 - 615 - 3 - - - 654 - 2136 - 3 - - - 3476 - 7606 - 3 - - - 9225 - 9729 - 3 - - - 19358 - 19359 - 1 - - - - - - - kind - parent - - - 12 - - - 2 - 3 - 1 - - - 3 - 4 - 4 - - - 5 - 9 - 3 - - - 17 - 35 - 3 - - - 47 - 69 - 3 - - - 69 - 205 - 3 - - - 301 - 367 - 3 - - - 385 - 586 - 3 - - - 606 - 1154 - 3 - - - 1719 - 5672 - 3 - - - 5912 - 9226 - 3 - - - 18820 - 18821 - 1 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 1 - - - 2 - 3 - 5 - - - 3 - 5 - 2 - - - 5 - 8 - 3 - - - 8 - 9 - 3 - - - 9 - 13 - 3 - - - 13 - 16 - 3 - - - 16 - 17 - 2 - - - 17 - 27 - 3 - - - 27 - 33 - 3 - - - 44 - 47 - 3 - - - 55 - 82 - 2 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 29362 - - - 2 - 3 - 6137 - - - 3 - 5 - 3650 - - - 5 - 82 - 2394 - - - - - - - parent - kind - - - 12 - - - 1 - 2 - 31418 - - - 2 - 3 - 5760 - - - 3 - 5 - 3597 - - - 5 - 11 - 768 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 29362 - - - 2 - 3 - 6137 - - - 3 - 5 - 3650 - - - 5 - 82 - 2394 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 6 - - - 2 - 3 - 20 - - - 5 - 6 - 3 - - - 7 - 8 - 11 - - - 10 - 15 - 7 - - - 15 - 27 - 7 - - - 27 - 70 - 7 - - - 85 - 262 - 7 - - - 314 - 1279 - 7 - - - 1720 - 24879 - 6 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 12 - - - 2 - 3 - 22 - - - 3 - 5 - 7 - - - 5 - 6 - 6 - - - 6 - 7 - 4 - - - 7 - 9 - 7 - - - 9 - 13 - 7 - - - 13 - 18 - 7 - - - 20 - 28 - 6 - - - 29 - 34 - 3 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 6 - - - 2 - 3 - 20 - - - 5 - 6 - 3 - - - 7 - 8 - 11 - - - 10 - 15 - 7 - - - 15 - 27 - 7 - - - 27 - 70 - 7 - - - 85 - 262 - 7 - - - 314 - 1279 - 7 - - - 1720 - 24879 - 6 - - - - - - - - - decls - 8690 - - - id - 8690 - - - kind - 5 - - - parent - 1951 - - - idx - 226 - - - - - id - kind - - - 12 - - - 1 - 2 - 8690 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 8690 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 8690 - - - - - - - kind - id - - - 12 - - - 280 - 281 - 1 - - - 479 - 480 - 1 - - - 1349 - 1350 - 1 - - - 1769 - 1770 - 1 - - - 4813 - 4814 - 1 - - - - - - - kind - parent - - - 12 - - - 196 - 197 - 1 - - - 336 - 337 - 1 - - - 478 - 479 - 1 - - - 483 - 484 - 1 - - - 1566 - 1567 - 1 - - - - - - - kind - idx - - - 12 - - - 2 - 3 - 1 - - - 39 - 40 - 1 - - - 105 - 106 - 1 - - - 219 - 220 - 1 - - - 225 - 226 - 1 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 1460 - - - 2 - 6 - 149 - - - 6 - 12 - 155 - - - 12 - 36 - 147 - - - 36 - 227 - 40 - - - - - - - parent - kind - - - 12 - - - 1 - 2 - 1463 - - - 2 - 3 - 111 - - - 3 - 4 - 185 - - - 4 - 5 - 141 - - - 5 - 6 - 51 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 1460 - - - 2 - 6 - 149 - - - 6 - 12 - 155 - - - 12 - 36 - 147 - - - 36 - 227 - 40 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 4 - - - 2 - 3 - 30 - - - 3 - 4 - 18 - - - 4 - 5 - 66 - - - 5 - 6 - 2 - - - 6 - 7 - 31 - - - 7 - 11 - 17 - - - 11 - 31 - 17 - - - 31 - 65 - 17 - - - 67 - 275 - 17 - - - 323 - 1952 - 7 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 7 - - - 2 - 3 - 113 - - - 3 - 4 - 69 - - - 4 - 5 - 35 - - - 5 - 6 - 2 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 4 - - - 2 - 3 - 30 - - - 3 - 4 - 18 - - - 4 - 5 - 66 - - - 5 - 6 - 2 - - - 6 - 7 - 31 - - - 7 - 11 - 17 - - - 11 - 31 - 17 - - - 31 - 65 - 17 - - - 67 - 275 - 17 - - - 323 - 1952 - 7 - - - - - - - - - specs - 7889 - - - id - 7889 - - - kind - 4 - - - parent - 3877 - - - idx - 108 - - - - - id - kind - - - 12 - - - 1 - 2 - 7889 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 7889 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 7889 - - - - - - - kind - id - - - 12 - - - 16 - 17 - 1 - - - 1349 - 1350 - 1 - - - 3056 - 3057 - 1 - - - 3468 - 3469 - 1 - - - - - - - kind - parent - - - 12 - - - 16 - 17 - 1 - - - 479 - 480 - 1 - - - 1333 - 1334 - 1 - - - 2049 - 2050 - 1 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 1 - - - 14 - 15 - 1 - - - 36 - 37 - 1 - - - 108 - 109 - 1 - - - - - - - parent - id - - - 12 - - - 1 - 2 - 3206 - - - 2 - 6 - 343 - - - 6 - 18 - 298 - - - 18 - 109 - 30 - - - - - - - parent - kind - - - 12 - - - 1 - 2 - 3877 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 3206 - - - 2 - 6 - 343 - - - 6 - 18 - 298 - - - 18 - 109 - 30 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 53 - - - 2 - 3 - 19 - - - 3 - 6 - 9 - - - 6 - 28 - 9 - - - 30 - 156 - 9 - - - 188 - 3878 - 9 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 72 - - - 2 - 3 - 22 - - - 3 - 4 - 13 - - - 4 - 5 - 1 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 53 - - - 2 - 3 - 19 - - - 3 - 6 - 9 - - - 6 - 28 - 9 - - - 30 - 156 - 9 - - - 188 - 3878 - 9 - - - - - - - - - scopes - 36775 - - - id - 36775 - - - kind - 3 - - - - - id - kind - - - 12 - - - 1 - 2 - 36775 - - - - - - - kind - id - - - 12 - - - 1 - 2 - 1 - - - 346 - 347 - 1 - - - 36428 - 36429 - 1 - - - - - - - - - scopenesting - 36774 - - - inner - 36774 - - - outer - 21713 - - - - - inner - outer - - - 12 - - - 1 - 2 - 36774 - - - - - - - outer - inner - - - 12 - - - 1 - 2 - 16964 - - - 2 - 3 - 2474 - - - 3 - 7 - 1759 - - - 7 - 347 - 516 - - - - - - - - - scopenodes - 36428 - - - node - 36428 - - - scope - 36428 - - - - - node - scope - - - 12 - - - 1 - 2 - 36428 - - - - - - - scope - node - - - 12 - - - 1 - 2 - 36428 - - - - - - - - - objects - 84909 - - - id - 84909 - - - kind - 9 - - - name - 30576 - - - - - id - kind - - - 12 - - - 1 - 2 - 84909 - - - - - - - id - name - - - 12 - - - 1 - 2 - 84909 - - - - - - - kind - id - - - 12 - - - 4 - 5 - 1 - - - 18 - 19 - 1 - - - 20 - 21 - 1 - - - 49 - 50 - 1 - - - 3468 - 3469 - 1 - - - 3602 - 3603 - 1 - - - 8857 - 8858 - 1 - - - 17793 - 17794 - 1 - - - 51098 - 51099 - 1 - - - - - - - kind - name - - - 12 - - - 4 - 5 - 1 - - - 18 - 19 - 1 - - - 20 - 21 - 1 - - - 38 - 39 - 1 - - - 203 - 204 - 1 - - - 3004 - 3005 - 1 - - - 8418 - 8419 - 1 - - - 10132 - 10133 - 1 - - - 10913 - 10914 - 1 - - - - - - - name - id - - - 12 - - - 1 - 2 - 25286 - - - 2 - 3 - 2613 - - - 3 - 20 - 2304 - - - 20 - 2222 - 373 - - - - - - - name - kind - - - 12 - - - 1 - 2 - 28809 - - - 2 - 6 - 1767 - - - - - - - - - objectscopes - 54774 - - - object - 54774 - - - scope - 13947 - - - - - object - scope - - - 12 - - - 1 - 2 - 54774 - - - - - - - scope - object - - - 12 - - - 1 - 2 - 7112 - - - 2 - 3 - 2972 - - - 3 - 4 - 1274 - - - 4 - 6 - 1264 - - - 6 - 15 - 1055 - - - 15 - 2694 - 270 - - - - - - - - - objecttypes - 84907 - - - object - 84907 - - - tp - 13462 - - - - - object - tp - - - 12 - - - 1 - 2 - 84907 - - - - - - - tp - object - - - 12 - - - 1 - 2 - 7893 - - - 2 - 3 - 2114 - - - 3 - 4 - 892 - - - 4 - 7 - 1190 - - - 7 - 25 - 1011 - - - 25 - 4267 - 362 - - - - - - - - - methodreceivers - 9873 - - - method - 9873 - - - receiver - 9873 - - - - - method - receiver - - - 12 - - - 1 - 2 - 9873 - - - - - - - receiver - method - - - 12 - - - 1 - 2 - 9873 - - - - - - - - - fieldstructs - 10934 - - - field - 10934 - - - struct - 2408 - - - - - field - struct - - - 12 - - - 1 - 2 - 10934 - - - - - - - struct - field - - - 12 - - - 1 - 2 - 260 - - - 2 - 3 - 677 - - - 3 - 4 - 468 - - - 4 - 5 - 292 - - - 5 - 6 - 194 - - - 6 - 8 - 208 - - - 8 - 13 - 199 - - - 13 - 65 - 110 - - - - - - - - - methodhosts - 838 - - - method - 699 - - - host - 258 - - - - - method - host - - - 12 - - - 1 - 2 - 625 - - - 2 - 3 - 56 - - - 3 - 16 - 18 - - - - - - - host - method - - - 12 - - - 1 - 2 - 99 - - - 2 - 3 - 56 - - - 3 - 4 - 37 - - - 4 - 5 - 20 - - - 5 - 6 - 15 - - - 6 - 11 - 21 - - - 11 - 53 - 10 - - - - - - - - - defs - 40703 - - - ident - 40703 - - - object - 40490 - - - - - ident - object - - - 12 - - - 1 - 2 - 40703 - - - - - - - object - ident - - - 12 - - - 1 - 2 - 40383 - - - 2 - 15 - 107 - - - - - - - - - uses - 195902 - - - ident - 195902 - - - object - 41616 - - - - - ident - object - - - 12 - - - 1 - 2 - 195902 - - - - - - - object - ident - - - 12 - - - 1 - 2 - 15493 - - - 2 - 3 - 9727 - - - 3 - 4 - 5056 - - - 4 - 5 - 2974 - - - 5 - 7 - 3203 - - - 7 - 14 - 3336 - - - 14 - 6833 - 1827 - - - - - - - - - types - 18132 - - - id - 18132 - - - kind - 37 - - - - - id - kind - - - 12 - - - 1 - 2 - 18132 - - - - - - - kind - id - - - 12 - - - 1 - 2 - 25 - - - 10 - 30 - 3 - - - 247 - 431 - 3 - - - 559 - 1904 - 3 - - - 2409 - 8011 - 3 - - - - - - - - - type_of - 397965 - - - expr - 397965 - - - tp - 8687 - - - - - expr - tp - - - 12 - - - 1 - 2 - 397965 - - - - - - - tp - expr - - - 12 - - - 1 - 2 - 2019 - - - 2 - 3 - 967 - - - 3 - 4 - 711 - - - 4 - 5 - 388 - - - 5 - 7 - 780 - - - 7 - 10 - 772 - - - 10 - 15 - 734 - - - 15 - 23 - 700 - - - 23 - 43 - 652 - - - 43 - 143 - 652 - - - 143 - 46949 - 312 - - - - - - - - - typename - 3567 - - - tp - 3567 - - - name - 2983 - - - - - tp - name - - - 12 - - - 1 - 2 - 3567 - - - - - - - name - tp - - - 12 - - - 1 - 2 - 2660 - - - 2 - 4 - 267 - - - 4 - 17 - 56 - - - - - - - - - key_type - 430 - - - map - 430 - - - tp - 149 - - - - - map - tp - - - 12 - - - 1 - 2 - 430 - - - - - - - tp - map - - - 12 - - - 1 - 2 - 106 - - - 2 - 3 - 17 - - - 3 - 6 - 12 - - - 6 - 13 - 12 - - - 20 - 136 - 2 - - - - - - - - - element_type - 1412 - - - container - 1412 - - - tp - 916 - - - - - container - tp - - - 12 - - - 1 - 2 - 1412 - - - - - - - tp - container - - - 12 - - - 1 - 2 - 773 - - - 2 - 3 - 94 - - - 3 - 68 - 49 - - - - - - - - - base_type - 1903 - - - ptr - 1903 - - - tp - 1903 - - - - - ptr - tp - - - 12 - - - 1 - 2 - 1903 - - - - - - - tp - ptr - - - 12 - - - 1 - 2 - 1903 - - - - - - - - - underlying_type - 3567 - - - named - 3567 - - - tp - 2755 - - - - - named - tp - - - 12 - - - 1 - 2 - 3567 - - - - - - - tp - named - - - 12 - - - 1 - 2 - 2582 - - - 2 - 154 - 173 - - - - - - - - - component_types - 36474 - - - parent - 11221 - - - index - 74 - - - name - 5540 - - - tp - 4295 - - - - - parent - index - - - 12 - - - 1 - 2 - 1198 - - - 2 - 3 - 3864 - - - 3 - 4 - 2953 - - - 4 - 5 - 1446 - - - 5 - 6 - 780 - - - 6 - 13 - 860 - - - 13 - 65 - 120 - - - - - - - parent - name - - - 12 - - - 1 - 2 - 8936 - - - 2 - 3 - 733 - - - 3 - 6 - 1008 - - - 6 - 64 - 544 - - - - - - - parent - tp - - - 12 - - - 1 - 2 - 2194 - - - 2 - 3 - 4537 - - - 3 - 4 - 2475 - - - 4 - 5 - 1110 - - - 5 - 12 - 848 - - - 12 - 52 - 57 - - - - - - - index - parent - - - 12 - - - 1 - 2 - 15 - - - 2 - 4 - 6 - - - 4 - 7 - 4 - - - 8 - 13 - 6 - - - 13 - 18 - 6 - - - 18 - 28 - 6 - - - 29 - 49 - 6 - - - 52 - 82 - 6 - - - 89 - 193 - 6 - - - 232 - 824 - 6 - - - 1505 - 6458 - 6 - - - 10274 - 10275 - 1 - - - - - - - index - name - - - 12 - - - 1 - 2 - 22 - - - 2 - 6 - 6 - - - 6 - 9 - 6 - - - 9 - 16 - 4 - - - 16 - 24 - 6 - - - 24 - 37 - 6 - - - 39 - 61 - 6 - - - 69 - 116 - 6 - - - 153 - 379 - 6 - - - 475 - 1260 - 6 - - - - - - - index - tp - - - 12 - - - 1 - 2 - 15 - - - 2 - 4 - 6 - - - 4 - 7 - 6 - - - 7 - 11 - 5 - - - 11 - 14 - 5 - - - 15 - 19 - 6 - - - 20 - 27 - 5 - - - 29 - 44 - 6 - - - 45 - 72 - 6 - - - 86 - 161 - 6 - - - 224 - 1436 - 6 - - - 1878 - 2153 - 2 - - - - - - - name - parent - - - 12 - - - 1 - 2 - 3941 - - - 2 - 3 - 851 - - - 3 - 6 - 484 - - - 6 - 8917 - 264 - - - - - - - name - index - - - 12 - - - 1 - 2 - 4284 - - - 2 - 3 - 737 - - - 3 - 6 - 440 - - - 6 - 28 - 79 - - - - - - - name - tp - - - 12 - - - 1 - 2 - 4599 - - - 2 - 3 - 518 - - - 3 - 21 - 416 - - - 21 - 3014 - 7 - - - - - - - tp - parent - - - 12 - - - 1 - 2 - 2055 - - - 2 - 3 - 812 - - - 3 - 4 - 408 - - - 4 - 6 - 394 - - - 6 - 11 - 342 - - - 11 - 2187 - 284 - - - - - - - tp - index - - - 12 - - - 1 - 2 - 2111 - - - 2 - 3 - 859 - - - 3 - 4 - 580 - - - 4 - 5 - 352 - - - 5 - 10 - 328 - - - 10 - 51 - 65 - - - - - - - tp - name - - - 12 - - - 1 - 2 - 2897 - - - 2 - 3 - 865 - - - 3 - 5 - 343 - - - 5 - 738 - 190 - - - - - - - - - array_length - 293 - - - tp - 293 - - - len - 103 - - - - - tp - len - - - 12 - - - 1 - 2 - 293 - - - - - - - len - tp - - - 12 - - - 1 - 2 - 62 - - - 2 - 3 - 15 - - - 3 - 4 - 7 - - - 4 - 7 - 8 - - - 7 - 15 - 7 - - - 15 - 26 - 4 - - - - - - - - - type_objects - 3567 - - - tp - 3567 - - - object - 3567 - - - - - tp - object - - - 12 - - - 1 - 2 - 3567 - - - - - - - object - tp - - - 12 - - - 1 - 2 - 3567 - - - - - - - - - packages - 346 - - - id - 346 - - - name - 281 - - - path - 346 - - - scope - 346 - - - - - id - name - - - 12 - - - 1 - 2 - 346 - - - - - - - id - path - - - 12 - - - 1 - 2 - 346 - - - - - - - id - scope - - - 12 - - - 1 - 2 - 346 - - - - - - - name - id - - - 12 - - - 1 - 2 - 255 - - - 2 - 3 - 23 - - - 3 - 40 - 3 - - - - - - - name - path - - - 12 - - - 1 - 2 - 255 - - - 2 - 3 - 23 - - - 3 - 40 - 3 - - - - - - - name - scope - - - 12 - - - 1 - 2 - 255 - - - 2 - 3 - 23 - - - 3 - 40 - 3 - - - - - - - path - id - - - 12 - - - 1 - 2 - 346 - - - - - - - path - name - - - 12 - - - 1 - 2 - 346 - - - - - - - path - scope - - - 12 - - - 1 - 2 - 346 - - - - - - - scope - id - - - 12 - - - 1 - 2 - 346 - - - - - - - scope - name - - - 12 - - - 1 - 2 - 346 - - - - - - - scope - path - - - 12 - - - 1 - 2 - 346 - - - - - - - - - modexprs - 9 - - - id - 9 - - - kind - 4 - - - parent - 2 - - - idx - 6 - - - - - id - kind - - - 12 - - - 1 - 2 - 9 - - - - - - - id - parent - - - 12 - - - 1 - 2 - 9 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 9 - - - - - - - kind - id - - - 12 - - - 1 - 2 - 3 - - - 6 - 7 - 1 - - - - - - - kind - parent - - - 12 - - - 1 - 2 - 3 - - - 2 - 3 - 1 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 3 - - - 5 - 6 - 1 - - - - - - - parent - id - - - 12 - - - 3 - 4 - 1 - - - 6 - 7 - 1 - - - - - - - parent - kind - - - 12 - - - 2 - 3 - 1 - - - 3 - 4 - 1 - - - - - - - parent - idx - - - 12 - - - 3 - 4 - 1 - - - 6 - 7 - 1 - - - - - - - idx - id - - - 12 - - - 1 - 2 - 3 - - - 2 - 3 - 3 - - - - - - - idx - kind - - - 12 - - - 1 - 2 - 4 - - - 2 - 3 - 2 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 3 - - - 2 - 3 - 3 - - - - - - - - - modtokens - 13 - - - token - 13 - - - parent - 7 - - - idx - 2 - - - - - token - parent - - - 12 - - - 1 - 2 - 13 - - - - - - - token - idx - - - 12 - - - 1 - 2 - 13 - - - - - - - parent - token - - - 12 - - - 1 - 2 - 1 - - - 2 - 3 - 6 - - - - - - - parent - idx - - - 12 - - - 1 - 2 - 1 - - - 2 - 3 - 6 - - - - - - - idx - token - - - 12 - - - 6 - 7 - 1 - - - 7 - 8 - 1 - - - - - - - idx - parent - - - 12 - - - 6 - 7 - 1 - - - 7 - 8 - 1 - - - - - - - - - errors - 0 - - - id - 0 - - - kind - 0 - - - msg - 0 - - - rawpos - 0 - - - file - 0 - - - line - 0 - - - col - 0 - - - package - 0 - - - idx - 0 - - - - - id - kind - - - 12 - - - 1 - 2 - 1 - - - - - - - id - msg - - - 12 - - - 1 - 2 - 1 - - - - - - - id - rawpos - - - 12 - - - 1 - 2 - 1 - - - - - - - id - file - - - 12 - - - 1 - 2 - 1 - - - - - - - id - line - - - 12 - - - 1 - 2 - 1 - - - - - - - id - col - - - 12 - - - 1 - 2 - 1 - - - - - - - id - package - - - 12 - - - 1 - 2 - 1 - - - - - - - id - idx - - - 12 - - - 1 - 2 - 1 - - - - - - - kind - id - - - 12 - - - - - - kind - msg - - - 12 - - - - - - kind - rawpos - - - 12 - - - - - - kind - file - - - 12 - - - - - - kind - line - - - 12 - - - - - - kind - col - - - 12 - - - - - - kind - package - - - 12 - - - - - - kind - idx - - - 12 - - - - - - msg - id - - - 12 - - - - - - msg - kind - - - 12 - - - - - - msg - rawpos - - - 12 - - - - - - msg - file - - - 12 - - - - - - msg - line - - - 12 - - - - - - msg - col - - - 12 - - - - - - msg - package - - - 12 - - - - - - msg - idx - - - 12 - - - - - - rawpos - id - - - 12 - - - - - - rawpos - kind - - - 12 - - - - - - rawpos - msg - - - 12 - - - - - - rawpos - file - - - 12 - - - - - - rawpos - line - - - 12 - - - - - - rawpos - col - - - 12 - - - - - - rawpos - package - - - 12 - - - - - - rawpos - idx - - - 12 - - - - - - file - id - - - 12 - - - - - - file - kind - - - 12 - - - - - - file - msg - - - 12 - - - - - - file - rawpos - - - 12 - - - - - - file - line - - - 12 - - - - - - file - col - - - 12 - - - - - - file - package - - - 12 - - - - - - file - idx - - - 12 - - - - - - line - id - - - 12 - - - - - - line - kind - - - 12 - - - - - - line - msg - - - 12 - - - - - - line - rawpos - - - 12 - - - - - - line - file - - - 12 - - - - - - line - col - - - 12 - - - - - - line - package - - - 12 - - - - - - line - idx - - - 12 - - - - - - col - id - - - 12 - - - - - - col - kind - - - 12 - - - - - - col - msg - - - 12 - - - - - - col - rawpos - - - 12 - - - - - - col - file - - - 12 - - - - - - col - line - - - 12 - - - - - - col - package - - - 12 - - - - - - col - idx - - - 12 - - - - - - package - id - - - 12 - - - - - - package - kind - - - 12 - - - - - - package - msg - - - 12 - - - - - - package - rawpos - - - 12 - - - - - - package - file - - - 12 - - - - - - package - line - - - 12 - - - - - - package - col - - - 12 - - - - - - package - idx - - - 12 - - - - - - idx - id - - - 12 - - - - - - idx - kind - - - 12 - - - - - - idx - msg - - - 12 - - - - - - idx - rawpos - - - 12 - - - - - - idx - file - - - 12 - - - - - - idx - line - - - 12 - - - - - - idx - col - - - 12 - - - - - - idx - package - - - 12 - - - - - - - - has_ellipsis - 268 - - - id - 268 - - - - - - diff --git a/ql/lib/go.qll b/ql/lib/go.qll deleted file mode 100644 index 7ebaa8a06..000000000 --- a/ql/lib/go.qll +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Provides classes for working with Go programs. - */ - -import Customizations -import semmle.go.Architectures -import semmle.go.AST -import semmle.go.Comments -import semmle.go.Concepts -import semmle.go.Decls -import semmle.go.Errors -import semmle.go.Expr -import semmle.go.Files -import semmle.go.GoMod -import semmle.go.HTML -import semmle.go.Locations -import semmle.go.Packages -import semmle.go.Scopes -import semmle.go.Stmt -import semmle.go.StringOps -import semmle.go.Types -import semmle.go.Util -import semmle.go.VariableWithFields -import semmle.go.controlflow.BasicBlocks -import semmle.go.controlflow.ControlFlowGraph -import semmle.go.controlflow.IR -import semmle.go.dataflow.DataFlow -import semmle.go.dataflow.DataFlow2 -import semmle.go.dataflow.GlobalValueNumbering -import semmle.go.dataflow.SSA -import semmle.go.dataflow.TaintTracking -import semmle.go.dataflow.TaintTracking2 -import semmle.go.frameworks.Beego -import semmle.go.frameworks.BeegoOrm -import semmle.go.frameworks.Chi -import semmle.go.frameworks.Couchbase -import semmle.go.frameworks.Echo -import semmle.go.frameworks.ElazarlGoproxy -import semmle.go.frameworks.Email -import semmle.go.frameworks.Encoding -import semmle.go.frameworks.EvanphxJsonPatch -import semmle.go.frameworks.Gin -import semmle.go.frameworks.Glog -import semmle.go.frameworks.GoRestfulHttp -import semmle.go.frameworks.K8sIoApimachineryPkgRuntime -import semmle.go.frameworks.K8sIoApiCoreV1 -import semmle.go.frameworks.K8sIoClientGo -import semmle.go.frameworks.Logrus -import semmle.go.frameworks.Macaron -import semmle.go.frameworks.Mux -import semmle.go.frameworks.NoSQL -import semmle.go.frameworks.Protobuf -import semmle.go.frameworks.Revel -import semmle.go.frameworks.Spew -import semmle.go.frameworks.SQL -import semmle.go.frameworks.Stdlib -import semmle.go.frameworks.SystemCommandExecutors -import semmle.go.frameworks.Testing -import semmle.go.frameworks.WebSocket -import semmle.go.frameworks.XNetHtml -import semmle.go.frameworks.XPath -import semmle.go.frameworks.Yaml -import semmle.go.frameworks.Zap -import semmle.go.security.FlowSources diff --git a/ql/lib/ideContextual.qll b/ql/lib/ideContextual.qll deleted file mode 100644 index b729aa81c..000000000 --- a/ql/lib/ideContextual.qll +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Provides classes and predicates related to contextual queries - * in the code viewer. - */ - -import go - -/** - * Returns the `File` matching the given source file name as encoded by the VS - * Code extension. - */ -cached -File getFileBySourceArchiveName(string name) { - // The name provided for a file in the source archive by the VS Code extension - // has some differences from the absolute path in the database: - // 1. colons are replaced by underscores - // 2. there's a leading slash, even for Windows paths: "C:/foo/bar" -> - // "/C_/foo/bar" - // 3. double slashes in UNC prefixes are replaced with a single slash - // We can handle 2 and 3 together by unconditionally adding a leading slash - // before replacing double slashes. - name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/") -} diff --git a/ql/lib/localDefinitions.ql b/ql/lib/localDefinitions.ql deleted file mode 100644 index 0ef61cbfd..000000000 --- a/ql/lib/localDefinitions.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Jump-to-definition links - * @description Generates use-definition pairs that provide the data - * for jump-to-definition in the code viewer. - * @kind definitions - * @id go/ide-jump-to-definition - * @tags ide-contextual-queries/local-definitions - */ - -import go -import ideContextual - -external string selectedSourceFile(); - -from Ident def, Ident use, Entity e -where - use.uses(e) and - def.declares(e) and - use.getFile() = getFileBySourceArchiveName(selectedSourceFile()) -select use, def, "V" diff --git a/ql/lib/localReferences.ql b/ql/lib/localReferences.ql deleted file mode 100644 index c0ed61ddf..000000000 --- a/ql/lib/localReferences.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Find-references links - * @description Generates use-definition pairs that provide the data - * for find-references in the code viewer. - * @kind definitions - * @id go/ide-find-references - * @tags ide-contextual-queries/local-references - */ - -import go -import ideContextual - -external string selectedSourceFile(); - -from Ident def, Ident use, Entity e -where - use.uses(e) and - def.declares(e) and - def.getFile() = getFileBySourceArchiveName(selectedSourceFile()) -select use, def, "V" diff --git a/ql/lib/printAst.ql b/ql/lib/printAst.ql deleted file mode 100644 index 9d6d5c2d7..000000000 --- a/ql/lib/printAst.ql +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @name Print AST - * @description Outputs a representation of a file's Abstract Syntax Tree. This - * query is used by the VS Code extension. - * @id go/print-ast - * @kind graph - * @tags ide-contextual-queries/print-ast - */ - -import go -import semmle.go.PrintAst -import ideContextual - -/** - * The source file to generate an AST from. - */ -external string selectedSourceFile(); - -/** - * Hook to customize the functions printed by this query. - */ -class Cfg extends PrintAstConfiguration { - override predicate shouldPrintFunction(FuncDecl func) { shouldPrintFile(func.getFile()) } - - override predicate shouldPrintFile(File file) { - file = getFileBySourceArchiveName(selectedSourceFile()) - } - - override predicate shouldPrintComments(File file) { none() } -} diff --git a/ql/lib/qlpack.lock.yml b/ql/lib/qlpack.lock.yml deleted file mode 100644 index 06dd07fc7..000000000 --- a/ql/lib/qlpack.lock.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -dependencies: {} -compiled: false -lockVersion: 1.0.0 diff --git a/ql/lib/qlpack.yml b/ql/lib/qlpack.yml deleted file mode 100644 index a4bdaa250..000000000 --- a/ql/lib/qlpack.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: codeql/go-all -version: 0.1.3-dev -groups: go -dbscheme: go.dbscheme -extractor: go -library: true -upgrades: upgrades diff --git a/ql/lib/semmle/go/AST.qll b/ql/lib/semmle/go/AST.qll deleted file mode 100644 index a9862ea33..000000000 --- a/ql/lib/semmle/go/AST.qll +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Provides classes for working with AST nodes. - */ - -import go - -/** - * An AST node. - */ -class AstNode extends @node, Locatable { - /** - * Gets the `i`th child node of this node. - * - * Note that the precise indices of child nodes are considered an implementation detail - * and are subject to change without notice. - */ - AstNode getChild(int i) { - result = this.(ExprParent).getChildExpr(i) or - result = this.(GoModExprParent).getChildGoModExpr(i) or - result = this.(StmtParent).getChildStmt(i) or - result = this.(DeclParent).getDecl(i) or - result = this.(GenDecl).getSpec(i) or - result = this.(FieldParent).getField(i) or - result = this.(File).getCommentGroup(i) or - result = this.(CommentGroup).getComment(i) - } - - /** - * Gets a child node of this node. - */ - AstNode getAChild() { result = getChild(_) } - - /** - * Gets the number of child nodes of this node. - */ - int getNumChild() { result = count(getAChild()) } - - /** - * Gets a child with the given index and of the given kind, if one exists. - * Note that a given parent can have multiple children with the same index but differing kind. - */ - private AstNode getChildOfKind(string kind, int i) { - kind = "expr" and result = this.(ExprParent).getChildExpr(i) - or - kind = "gomodexpr" and result = this.(GoModExprParent).getChildGoModExpr(i) - or - kind = "stmt" and result = this.(StmtParent).getChildStmt(i) - or - kind = "decl" and result = this.(DeclParent).getDecl(i) - or - kind = "spec" and result = this.(GenDecl).getSpec(i) - or - kind = "field" and result = this.(FieldParent).getField(i) - or - kind = "commentgroup" and result = this.(File).getCommentGroup(i) - or - kind = "comment" and result = this.(CommentGroup).getComment(i) - } - - /** - * Get an AstNode child, ordered by child kind and then by index. - */ - AstNode getUniquelyNumberedChild(int index) { - result = - rank[index + 1](AstNode child, string kind, int i | - child = getChildOfKind(kind, i) - | - child order by kind, i - ) - } - - /** Gets the parent node of this AST node, if any. */ - AstNode getParent() { this = result.getAChild() } - - /** Gets the parent node of this AST node, but without crossing function boundaries. */ - private AstNode parentInSameFunction() { - result = getParent() and - not this instanceof FuncDef - } - - /** Gets the innermost function definition to which this AST node belongs, if any. */ - FuncDef getEnclosingFunction() { result = getParent().parentInSameFunction*() } - - /** - * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. - */ - final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") } - - /** - * Gets the name of a primary CodeQL class to which this node belongs. - * - * For most nodes, this is simply the most precise syntactic category to which they belong; - * for example, `AddExpr` is a primary class, but `BinaryExpr` is not. - * - * For identifiers and selector expressions, the class describing what kind of entity they refer - * to (for example `FunctionName` or `TypeName`) is also considered primary. For such nodes, - * this predicate has multiple values. - */ - string getAPrimaryQlClass() { result = "???" } - - override string toString() { result = "AST node" } -} - -/** - * An AST node whose children include expressions. - */ -class ExprParent extends @exprparent, AstNode { - /** - * Gets the `i`th child expression of this node. - * - * Note that the precise indices of child expressions are considered an implementation detail - * and are subject to change without notice. - */ - Expr getChildExpr(int i) { exprs(result, _, this, i) } - - /** - * Gets an expression that is a child node of this node in the AST. - */ - Expr getAChildExpr() { result = getChildExpr(_) } - - /** - * Gets the number of child expressions of this node. - */ - int getNumChildExpr() { result = count(getAChildExpr()) } -} - -/** - * An AST node whose children include go.mod expressions. - */ -class GoModExprParent extends @modexprparent, AstNode { - /** - * Gets the `i`th child expression of this node. - * - * Note that the precise indices of child expressions are considered an implementation detail - * and are subject to change without notice. - */ - GoModExpr getChildGoModExpr(int i) { modexprs(result, _, this, i) } - - /** - * Gets an expression that is a child node of this node in the AST. - */ - GoModExpr getAChildGoModExpr() { result = getChildGoModExpr(_) } - - /** - * Gets the number of child expressions of this node. - */ - int getNumChildGoModExpr() { result = count(getAChildGoModExpr()) } -} - -/** - * An AST node whose children include statements. - */ -class StmtParent extends @stmtparent, AstNode { - /** - * Gets the `i`th child statement of this node. - * - * Note that the precise indices of child statements are considered an implementation detail - * and are subject to change without notice. - */ - Stmt getChildStmt(int i) { stmts(result, _, this, i) } - - /** - * Gets a statement that is a child node of this node in the AST. - */ - Stmt getAChildStmt() { result = getChildStmt(_) } - - /** - * Gets the number of child statements of this node. - */ - int getNumChildStmt() { result = count(getAChildStmt()) } -} - -/** - * An AST node whose children include declarations. - */ -class DeclParent extends @declparent, AstNode { - /** - * Gets the `i`th child declaration of this node. - * - * Note that the precise indices of declarations are considered an implementation detail - * and are subject to change without notice. - */ - Decl getDecl(int i) { decls(result, _, this, i) } - - /** - * Gets a child declaration of this node in the AST. - */ - Decl getADecl() { result = getDecl(_) } - - /** - * Gets the number of child declarations of this node. - */ - int getNumDecl() { result = count(getADecl()) } -} - -/** - * An AST node whose children include fields. - */ -class FieldParent extends @fieldparent, AstNode { - /** - * Gets the `i`th field of this node. - * - * Note that the precise indices of fields are considered an implementation detail - * and are subject to change without notice. - */ - FieldBase getField(int i) { fields(result, this, i) } - - /** - * Gets a child field of this node in the AST. - */ - FieldBase getAField() { result = getField(_) } - - /** - * Gets the number of child fields of this node. - */ - int getNumFields() { result = count(getAField()) } -} - -/** - * An AST node whose children include type parameter declarations. - */ -class TypeParamDeclParent extends @typeparamdeclparent, AstNode { - /** - * Gets the `i`th type parameter declaration of this node. - * - * Note that the precise indices of type parameters are considered an implementation detail - * and are subject to change without notice. - */ - TypeParamDecl getTypeParameterDecl(int i) { typeparamdecls(result, this, i) } - - /** - * Gets a child field of this node in the AST. - */ - TypeParamDecl getATypeParameterDecl() { result = this.getTypeParameterDecl(_) } -} - -/** - * An AST node which may induce a scope. - * - * The following nodes may induce scopes: - * - * - files - * - block statements, `if` statements, `switch` statements, `case` clauses, comm clauses, loops - * - function type expressions - * - * Note that functions themselves do not induce a scope, it is their type declaration that induces - * the scope. - */ -class ScopeNode extends @scopenode, AstNode { - /** Gets the scope induced by this node, if any. */ - LocalScope getScope() { scopenodes(this, result) } -} diff --git a/ql/lib/semmle/go/Architectures.qll b/ql/lib/semmle/go/Architectures.qll deleted file mode 100644 index e509de4bc..000000000 --- a/ql/lib/semmle/go/Architectures.qll +++ /dev/null @@ -1,37 +0,0 @@ -/** Provides classes for working with architectures. */ - -import go - -/** - * An architecture that is valid in a build constraint. - * - * Information obtained from - * https://github.com/golang/go/blob/e125ccd10ea191101dbc31f0dd39a98f9d3ab929/src/go/types/gccgosizes.go - * where the first field of the struct is 4 for 32-bit architectures - * and 8 for 64-bit architectures. - */ -class Architecture extends string { - int bitSize; - - Architecture() { - this in [ - "386", "amd64p32", "arm", "armbe", "m64k", "mips", "mipsle", "mips64p32", "mips64p32le", - "nios2", "ppc", "riscv", "s390", "sh", "shbe", "sparc" - ] and - bitSize = 32 - or - this in [ - "alpha", "amd64", "arm64", "arm64be", "ia64", "mips64", "mips64le", "ppc64", "ppc64le", - "riscv64", "s390x", "sparc64", "wasm" - ] and - bitSize = 64 - } - - /** - * Gets the integer and pointer type width for this architecture. - * - * As of the time of writing, this appears to always be identical -- there aren't - * Go architectures with 64-bit pointers but 32-bit ints, for example. - */ - int getBitSize() { result = bitSize } -} diff --git a/ql/lib/semmle/go/Comments.qll b/ql/lib/semmle/go/Comments.qll deleted file mode 100644 index 5026047cc..000000000 --- a/ql/lib/semmle/go/Comments.qll +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Provides classes for working with code comments. - */ - -import go - -/** - * A code comment. - * - * Examples: - * - *
- * // a line comment
- * /* a block
- *   comment */
- * 
- */ -class Comment extends @comment, AstNode { - /** - * Gets the text of this comment, not including delimiters. - */ - string getText() { comments(this, _, _, _, result) } - - /** - * Gets the comment group to which this comment belongs. - */ - CommentGroup getGroup() { this = result.getAComment() } - - override string toString() { result = "comment" } - - override string getAPrimaryQlClass() { result = "Comment" } -} - -/** - * A comment group, that is, a sequence of comments without any intervening tokens or - * empty lines. - * - * Examples: - * - *
- * // a line comment
- * // another line comment
- *
- * // a line comment
- * /* a block
- *   comment */
- *
- * /* a block
- * comment */
- * /* another block comment */
- * 
- */ -class CommentGroup extends @comment_group, AstNode { - /** - * Gets the file to which this comment group belongs. - */ - override File getParent() { this = result.getACommentGroup() } - - /** Gets the `i`th comment in this group (0-based indexing). */ - Comment getComment(int i) { comments(result, _, this, i, _) } - - /** Gets a comment in this group. */ - Comment getAComment() { result = this.getComment(_) } - - /** Gets the number of comments in this group. */ - int getNumComment() { result = count(this.getAComment()) } - - override string toString() { result = "comment group" } - - override string getAPrimaryQlClass() { result = "CommentGroup" } -} - -/** - * A program element to which a documentation comment group may be attached: - * a file, a field, a specifier, a generic declaration, a function declaration - * or a go.mod expression. - * - * Examples: - * - * ```go - * // function documentation - * func double(x int) int { return 2 * x } - * - * // generic declaration documentation - * const ( - * // specifier documentation - * size int64 = 1024 - * eof = -1 // not specifier documentation - * ) - * ``` - */ -class Documentable extends AstNode, @documentable { - /** Gets the documentation comment group attached to this element, if any. */ - DocComment getDocumentation() { this = result.getDocumentedElement() } -} - -/** - * A comment group that is attached to a program element as documentation. - * - * Examples: - * - * ```go - * // function documentation - * func double(x int) int { return 2 * x } - * - * // generic declaration documentation - * const ( - * // specifier documentation - * size int64 = 1024 - * eof = -1 // not specifier documentation - * ) - * ``` - */ -class DocComment extends CommentGroup { - Documentable node; - - DocComment() { doc_comments(node, this) } - - /** Gets the program element documented by this comment group. */ - Documentable getDocumentedElement() { result = node } - - override string getAPrimaryQlClass() { result = "DocComment" } -} - -/** - * A single-line comment starting with `//`. - * - * Examples: - * - * ```go - * // Single line comment - * ``` - */ -class SlashSlashComment extends @slashslashcomment, Comment { - override string getAPrimaryQlClass() { result = "SlashSlashComment" } -} - -/** - * A block comment starting with `/*` and ending with */. - * - * Examples: - * - *
- * /* a block
- *   comment */
- * 
- */ -class SlashStarComment extends @slashstarcomment, Comment { - override string getAPrimaryQlClass() { result = "SlashStarComment" } -} - -/** - * A single-line comment starting with `//`. - * - * Examples: - * - * ```go - * // Single line comment - * ``` - */ -class LineComment = SlashSlashComment; - -/** - * A block comment starting with `/*` and ending with */. - * - * Examples: - * - *
- * /* a block
- *   comment */
- * 
- */ -class BlockComment = SlashStarComment; - -/** Holds if `c` starts at `line`, `col` in `f`, and precedes the package declaration. */ -private predicate isInitialComment(Comment c, File f, int line, int col) { - c.hasLocationInfo(f.getAbsolutePath(), line, col, _, _) and - line < f.getPackageNameExpr().getLocation().getStartLine() -} - -/** Gets the `i`th initial comment in `f` (0-based). */ -private Comment getInitialComment(File f, int i) { - result = - rank[i + 1](Comment c, int line, int col | - isInitialComment(c, f, line, col) - | - c order by line, col - ) -} - -/** - * A build constraint comment of the form `// +build ...` or `//go:build ...`. - * - * Examples: - * - * ```go - * // +build darwin freebsd netbsd openbsd - * // +build !linux - * ``` - */ -class BuildConstraintComment extends LineComment { - BuildConstraintComment() { - // a line comment preceding the package declaration, itself only preceded by - // line comments - exists(File f, int i | - // correctness of the placement of the build constraint is not checked here; - // this is more lax than the actual rules for build constraints - this = getInitialComment(f, i) and - not getInitialComment(f, [0 .. i - 1]) instanceof BlockComment - ) and - ( - // comment text starts with `+build` or `go:build` - this.getText().regexpMatch("\\s*\\+build.*") - or - this.getText().regexpMatch("\\s*go:build.*") - ) - } - - override string getAPrimaryQlClass() { result = "BuildConstraintComment" } - - /** Gets the body of this build constraint. */ - string getConstraintBody() { result = this.getText().splitAt("build ", 1) } - - /** Gets a disjunct of this build constraint. */ - string getADisjunct() { result = this.getConstraintBody().splitAt(" ") } -} diff --git a/ql/lib/semmle/go/Concepts.qll b/ql/lib/semmle/go/Concepts.qll deleted file mode 100644 index f3920c2ec..000000000 --- a/ql/lib/semmle/go/Concepts.qll +++ /dev/null @@ -1,475 +0,0 @@ -/** - * Provides abstract classes representing generic concepts such as file system - * access or system command execution, for which individual framework libraries - * provide concrete subclasses. - */ - -import go -import semmle.go.dataflow.FunctionInputsAndOutputs -import semmle.go.concepts.HTTP -import semmle.go.concepts.GeneratedFile - -/** - * A data-flow node that executes an operating system command, - * for instance by spawning a new process. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `SystemCommandExecution::Range` instead. - */ -class SystemCommandExecution extends DataFlow::Node { - SystemCommandExecution::Range self; - - SystemCommandExecution() { this = self } - - /** Gets the argument that specifies the command to be executed. */ - DataFlow::Node getCommandName() { result = self.getCommandName() } - - /** Holds if this node is sanitized whenever it follows `--` in an argument list. */ - predicate doubleDashIsSanitizing() { self.doubleDashIsSanitizing() } -} - -/** Provides a class for modeling new system-command execution APIs. */ -module SystemCommandExecution { - /** - * A data-flow node that executes an operating system command, - * for instance by spawning a new process. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `SystemCommandExecution` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets the argument that specifies the command to be executed. */ - abstract DataFlow::Node getCommandName(); - - /** Holds if this node is sanitized whenever it follows `--` in an argument list. */ - predicate doubleDashIsSanitizing() { none() } - } -} - -/** - * An instantiation of a template; that is, a call which fills out a template with data. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `TemplateInstantiation::Range` instead. - */ -class TemplateInstantiation extends DataFlow::Node { - TemplateInstantiation::Range self; - - TemplateInstantiation() { this = self } - - /** - * Gets the argument to this template instantiation that is the template being - * instantiated. - */ - DataFlow::Node getTemplateArgument() { result = self.getTemplateArgument() } - - /** - * Gets an argument to this template instantiation that is data being inserted - * into the template. - */ - DataFlow::Node getADataArgument() { result = self.getADataArgument() } -} - -/** Provides a class for modeling new template-instantiation APIs. */ -module TemplateInstantiation { - /** - * An instantiation of a template; that is, a call which fills out a template with data. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `TemplateInstantiation` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the argument to this template instantiation that is the template being - * instantiated. - */ - abstract DataFlow::Node getTemplateArgument(); - - /** - * Gets an argument to this template instantiation that is data being inserted - * into the template. - */ - abstract DataFlow::Node getADataArgument(); - } -} - -/** - * A data-flow node that performs a file system access, including reading and writing data, - * creating and deleting files and folders, checking and updating permissions, and so on. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `FileSystemAccess::Range` instead. - */ -class FileSystemAccess extends DataFlow::Node { - FileSystemAccess::Range self; - - FileSystemAccess() { this = self } - - /** Gets an argument to this file system access that is interpreted as a path. */ - DataFlow::Node getAPathArgument() { result = self.getAPathArgument() } -} - -/** Provides a class for modeling new file-system access APIs. */ -module FileSystemAccess { - /** - * A data-flow node that performs a file system access, including reading and writing data, - * creating and deleting files and folders, checking and updating permissions, and so on. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `FileSystemAccess` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets an argument to this file system access that is interpreted as a path. */ - abstract DataFlow::Node getAPathArgument(); - } -} - -/** A function that escapes meta-characters to prevent injection attacks. */ -class EscapeFunction extends Function { - EscapeFunction::Range self; - - EscapeFunction() { this = self } - - /** - * The context that this function escapes for. - * - * Currently, this can be "js", "html", or "url". - */ - string kind() { result = self.kind() } -} - -/** Provides a class for modeling new escape-function APIs. */ -module EscapeFunction { - /** - * A function that escapes meta-characters to prevent injection attacks. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `EscapeFunction' instead. - */ - abstract class Range extends Function { - /** - * The context that this function escapes for. - * - * Currently, this can be `js', `html', or `url'. - */ - abstract string kind(); - } -} - -/** - * A function that escapes a string so it can be safely included in a - * JavaScript string literal. - */ -class JsEscapeFunction extends EscapeFunction { - JsEscapeFunction() { self.kind() = "js" } -} - -/** - * A function that escapes a string so it can be safely included in an - * the body of an HTML element, for example, replacing `{}` in - * `

{}

`. - */ -class HtmlEscapeFunction extends EscapeFunction { - HtmlEscapeFunction() { self.kind() = "html" } -} - -/** - * A function that escapes a string so it can be safely included as part - * of a URL. - */ -class UrlEscapeFunction extends EscapeFunction { - UrlEscapeFunction() { self.kind() = "url" } -} - -/** - * A node whose value is interpreted as a part of a regular expression. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexpPattern::Range` instead. - */ -class RegexpPattern extends DataFlow::Node { - RegexpPattern::Range self; - - RegexpPattern() { this = self } - - /** - * Gets the node where this pattern is parsed as a part of a regular - * expression. - */ - DataFlow::Node getAParse() { result = self.getAParse() } - - /** - * Gets this regexp pattern as a string. - */ - string getPattern() { result = self.getPattern() } - - /** - * Gets a use of this pattern, either as itself in an argument to a function or as a compiled - * regexp object. - */ - DataFlow::Node getAUse() { result = self.getAUse() } -} - -/** Provides a class for modeling new regular-expression APIs. */ -module RegexpPattern { - /** - * A node whose value is interpreted as a part of a regular expression. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `RegexpPattern' instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets a node where the pattern of this node is parsed as a part of - * a regular expression. - */ - abstract DataFlow::Node getAParse(); - - /** - * Gets this regexp pattern as a string. - */ - abstract string getPattern(); - - /** - * Gets a use of this pattern, either as itself in an argument to a function or as a compiled - * regexp object. - */ - abstract DataFlow::Node getAUse(); - } -} - -/** - * A function that matches a regexp with a string or byte slice. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexpMatchFunction::Range` instead. - */ -class RegexpMatchFunction extends Function { - RegexpMatchFunction::Range self; - - RegexpMatchFunction() { this = self } - - /** - * Gets the function input that is the regexp being matched. - */ - FunctionInput getRegexpArg() { result = self.getRegexpArg() } - - /** - * Gets the regexp pattern that is used in the call to this function `call`. - */ - RegexpPattern getRegexp(DataFlow::CallNode call) { - result.getAUse() = this.getRegexpArg().getNode(call) - } - - /** - * Gets the function input that is the string being matched against. - */ - FunctionInput getValue() { result = self.getValue() } - - /** - * Gets the function output that is the Boolean result of the match function. - */ - FunctionOutput getResult() { result = self.getResult() } -} - -/** Provides a class for modeling new regular-expression matcher APIs. */ -module RegexpMatchFunction { - /** - * A function that matches a regexp with a string or byte slice. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `RegexpPattern' instead. - */ - abstract class Range extends Function { - /** - * Gets the function input that is the regexp being matched. - */ - abstract FunctionInput getRegexpArg(); - - /** - * Gets the function input that is the string being matched against. - */ - abstract FunctionInput getValue(); - - /** - * Gets the Boolean result of the match function. - */ - abstract FunctionOutput getResult(); - } -} - -/** - * A function that uses a regexp to replace parts of a string or byte slice. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexpReplaceFunction::Range` instead. - */ -class RegexpReplaceFunction extends Function { - RegexpReplaceFunction::Range self; - - RegexpReplaceFunction() { this = self } - - /** - * Gets the function input that is the regexp that matches text to replace. - */ - FunctionInput getRegexpArg() { result = self.getRegexpArg() } - - /** - * Gets the regexp pattern that is used to match patterns to replace in the call to this function - * `call`. - */ - RegexpPattern getRegexp(DataFlow::CallNode call) { - result.getAUse() = call.(DataFlow::MethodCallNode).getReceiver() - } - - /** - * Gets the function input corresponding to the source value, that is, the value that is having - * its contents replaced. - */ - FunctionInput getSource() { result = self.getSource() } - - /** - * Gets the function output corresponding to the result, that is, the value after replacement has - * occurred. - */ - FunctionOutput getResult() { result = self.getResult() } -} - -/** Provides a class for modeling new regular-expression replacer APIs. */ -module RegexpReplaceFunction { - /** - * A function that uses a regexp to replace parts of a string or byte slice. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `RegexpReplaceFunction' instead. - */ - abstract class Range extends Function { - /** - * Gets the function input that is the regexp that matches text to replace. - */ - abstract FunctionInput getRegexpArg(); - - /** - * Gets the function input corresponding to the source value, that is, the value that is having - * its contents replaced. - */ - abstract FunctionInput getSource(); - - /** - * Gets the function output corresponding to the result, that is, the value after replacement - * has occurred. - */ - abstract FunctionOutput getResult(); - } -} - -/** - * A call to a logging mechanism. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `LoggerCall::Range` instead. - */ -class LoggerCall extends DataFlow::Node { - LoggerCall::Range self; - - LoggerCall() { this = self } - - /** Gets a node that is a part of the logged message. */ - DataFlow::Node getAMessageComponent() { result = self.getAMessageComponent() } -} - -/** Provides a class for modeling new logging APIs. */ -module LoggerCall { - /** - * A call to a logging mechanism. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `LoggerCall` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets a node that is a part of the logged message. */ - abstract DataFlow::Node getAMessageComponent(); - } -} - -/** - * A function that encodes data into a binary or textual format. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `MarshalingFunction::Range` instead. - */ -class MarshalingFunction extends Function { - MarshalingFunction::Range self; - - MarshalingFunction() { this = self } - - /** Gets an input that is encoded by this function. */ - FunctionInput getAnInput() { result = self.getAnInput() } - - /** Gets the output that contains the encoded data produced by this function. */ - FunctionOutput getOutput() { result = self.getOutput() } - - /** Gets an identifier for the format this function encodes into, such as "JSON". */ - string getFormat() { result = self.getFormat() } -} - -/** Provides a class for modeling new marshaling APIs. */ -module MarshalingFunction { - /** - * A function that encodes data into a binary or textual format. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `MarshalingFunction` instead. - */ - abstract class Range extends Function { - /** Gets an input that is encoded by this function. */ - abstract FunctionInput getAnInput(); - - /** Gets the output that contains the encoded data produced by this function. */ - abstract FunctionOutput getOutput(); - - /** Gets an identifier for the format this function encodes into, such as "JSON". */ - abstract string getFormat(); - } -} - -/** - * A function that decodes data from a binary or textual format. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `UnmarshalingFunction::Range` instead. - */ -class UnmarshalingFunction extends Function { - UnmarshalingFunction::Range self; - - UnmarshalingFunction() { this = self } - - /** Gets an input that is decoded by this function. */ - FunctionInput getAnInput() { result = self.getAnInput() } - - /** Gets the output that contains the decoded data produced by this function. */ - FunctionOutput getOutput() { result = self.getOutput() } - - /** Gets an identifier for the format this function decodes from, such as "JSON". */ - string getFormat() { result = self.getFormat() } -} - -/** Provides a class for modeling new unmarshaling APIs. */ -module UnmarshalingFunction { - /** - * A function that decodes data from a binary or textual format. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `UnmarshalingFunction` instead. - */ - abstract class Range extends Function { - /** Gets an input that is decoded by this function. */ - abstract FunctionInput getAnInput(); - - /** Gets the output that contains the decoded data produced by this function. */ - abstract FunctionOutput getOutput(); - - /** Gets an identifier for the format this function decodes from, such as "JSON". */ - abstract string getFormat(); - } -} diff --git a/ql/lib/semmle/go/Decls.qll b/ql/lib/semmle/go/Decls.qll deleted file mode 100644 index 0623aa1cc..000000000 --- a/ql/lib/semmle/go/Decls.qll +++ /dev/null @@ -1,664 +0,0 @@ -/** - * Provides classes for working with declarations. - */ - -import go - -/** - * A declaration. - */ -class Decl extends @decl, ExprParent, StmtParent, FieldParent { - /** - * Gets the kind of this declaration, which is an integer value representing the declaration's - * node type. - * - * Note that the mapping from node types to integer kinds is considered an implementation detail - * and subject to change without notice. - */ - int getKind() { decls(this, result, _, _) } - - /** - * Holds if the execution of this statement may produce observable side effects. - * - * Memory allocation is not considered an observable side effect. - */ - predicate mayHaveSideEffects() { none() } -} - -/** - * A bad declaration, that is, a declaration that cannot be parsed. - */ -class BadDecl extends @baddecl, Decl { - override string toString() { result = "bad declaration" } - - override string getAPrimaryQlClass() { result = "BadDecl" } -} - -/** - * A generic declaration. - */ -class GenDecl extends @gendecl, Decl, Documentable { - /** Gets the `i`th declaration specifier in this declaration (0-based). */ - Spec getSpec(int i) { specs(result, _, this, i) } - - /** Gets a declaration specifier in this declaration. */ - Spec getASpec() { result = this.getSpec(_) } - - /** Gets the number of declaration specifiers in this declaration. */ - int getNumSpec() { result = count(this.getASpec()) } - - override predicate mayHaveSideEffects() { this.getASpec().mayHaveSideEffects() } - - override string getAPrimaryQlClass() { result = "GenDecl" } -} - -/** - * An import declaration. - */ -class ImportDecl extends @importdecl, GenDecl { - override string toString() { result = "import declaration" } - - override string getAPrimaryQlClass() { result = "ImportDecl" } -} - -/** - * A constant declaration. - */ -class ConstDecl extends @constdecl, GenDecl { - override string toString() { result = "constant declaration" } - - override string getAPrimaryQlClass() { result = "ConstDecl" } -} - -/** - * A type declaration. - */ -class TypeDecl extends @typedecl, GenDecl { - override string toString() { result = "type declaration" } - - override string getAPrimaryQlClass() { result = "TypeDecl" } -} - -/** - * A variable declaration. - */ -class VarDecl extends @vardecl, GenDecl { - override string toString() { result = "variable declaration" } - - override string getAPrimaryQlClass() { result = "VarDecl" } -} - -/** - * A function definition, that is, either a function declaration or - * a function literal. - */ -class FuncDef extends @funcdef, StmtParent, ExprParent { - /** Gets the body of the defined function, if any. */ - BlockStmt getBody() { none() } - - /** Gets the name of the defined function, if any. */ - string getName() { none() } - - /** Gets the expression denoting the type of this function. */ - FuncTypeExpr getTypeExpr() { none() } - - /** Gets the type of this function. */ - SignatureType getType() { none() } - - /** Gets the scope induced by this function. */ - FunctionScope getScope() { result.getFunction() = this } - - /** Gets a `defer` statement in this function. */ - DeferStmt getADeferStmt() { result.getEnclosingFunction() = this } - - /** Gets the `i`th result variable of this function. */ - ResultVariable getResultVar(int i) { result.isResultOf(this, i) } - - /** Gets a result variable of this function. */ - ResultVariable getAResultVar() { result.getFunction() = this } - - /** - * Gets the `i`th parameter of this function. - * - * The receiver variable, if any, is considered to be the -1st parameter. - */ - Parameter getParameter(int i) { result.isParameterOf(this, i) } - - /** Gets a parameter of this function. */ - Parameter getAParameter() { result.getFunction() = this } - - /** - * Gets the number of parameters of this function. - */ - int getNumParameter() { result = count(this.getAParameter()) } - - /** - * Gets a call to this function. - */ - DataFlow::CallNode getACall() { result.getACallee() = this } - - /** Holds if this function is variadic. */ - predicate isVariadic() { this.getType().isVariadic() } - - override string getAPrimaryQlClass() { result = "FuncDef" } -} - -/** - * A function declaration. - */ -class FuncDecl extends @funcdecl, Decl, Documentable, FuncDef, TypeParamDeclParent { - /** Gets the identifier denoting the name of this function. */ - Ident getNameExpr() { result = this.getChildExpr(0) } - - override string getName() { result = this.getNameExpr().getName() } - - override FuncTypeExpr getTypeExpr() { result = this.getChildExpr(1) } - - override SignatureType getType() { result = this.getNameExpr().getType() } - - /** Gets the body of this function, if any. */ - override BlockStmt getBody() { result = this.getChildStmt(2) } - - /** Gets the function declared by this function declaration. */ - DeclaredFunction getFunction() { this = result.getFuncDecl() } - - override string toString() { result = "function declaration" } - - override string getAPrimaryQlClass() { result = "FuncDecl" } -} - -/** - * A method declaration. - */ -class MethodDecl extends FuncDecl { - ReceiverDecl recv; - - MethodDecl() { recv.getFunction() = this } - - /** - * Gets the receiver declaration of this method. - * - * For example, the receiver declaration of - * - * ``` - * func (p *Rectangle) Area() float64 { ... } - * ``` - * - * is `p *Rectangle`. - */ - ReceiverDecl getReceiverDecl() { result = recv } - - /** - * Gets the receiver type of this method. - * - * For example, the receiver type of - * - * ``` - * func (p *Rectangle) Area() float64 { ... } - * ``` - * - * is `*Rectangle`. - */ - Type getReceiverType() { result = this.getReceiverDecl().getType() } - - /** - * Gets the receiver base type of this method. - * - * For example, the receiver base type of - * - * ``` - * func (p *Rectangle) Area() float64 { ... } - * ``` - * - * is `Rectangle`. - */ - NamedType getReceiverBaseType() { - result = this.getReceiverType() or - result = this.getReceiverType().(PointerType).getBaseType() - } - - /** - * Gets the receiver variable of this method. - * - * For example, the receiver variable of - * - * ``` - * func (p *Rectangle) Area() float64 { ... } - * ``` - * - * is the variable `p`. - */ - ReceiverVariable getReceiver() { result.getFunction() = this } - - override string getAPrimaryQlClass() { result = "MethodDecl" } -} - -/** - * A declaration specifier. - */ -class Spec extends @spec, ExprParent, Documentable { - /** Gets the declaration to which this specifier belongs */ - Decl getParentDecl() { specs(this, _, result, _) } - - /** - * Gets the kind of this specifier, which is an integer value representing the specifier's - * node type. - * - * Note that the mapping from node types to integer kinds is considered an implementation detail - * and subject to change without notice. - */ - int getKind() { specs(this, result, _, _) } - - /** - * Holds if the execution of this specifier may produce observable side effects. - * - * Memory allocation is not considered an observable side effect. - */ - predicate mayHaveSideEffects() { none() } - - override string getAPrimaryQlClass() { result = "Spec" } -} - -/** - * An import specifier. - */ -class ImportSpec extends @importspec, Spec { - /** Gets the identifier denoting the imported name. */ - Ident getNameExpr() { result = this.getChildExpr(0) } - - /** Gets the imported name. */ - string getName() { result = this.getNameExpr().getName() } - - /** Gets the string literal denoting the imported path. */ - StringLit getPathExpr() { result = this.getChildExpr(1) } - - /** Gets the imported path. */ - string getPath() { result = this.getPathExpr().getValue() } - - override string toString() { result = "import specifier" } - - override string getAPrimaryQlClass() { result = "ImportSpec" } -} - -/** - * A constant or variable declaration specifier. - */ -class ValueSpec extends @valuespec, Spec { - /** Gets the identifier denoting the `i`th name declared by this specifier (0-based). */ - Ident getNameExpr(int i) { - i >= 0 and - result = this.getChildExpr(-(i + 1)) - } - - /** Holds if this specifier is a part of a constant declaration. */ - predicate isConstSpec() { this.getParentDecl() instanceof ConstDecl } - - /** Gets an identifier denoting a name declared by this specifier. */ - Ident getANameExpr() { result = this.getNameExpr(_) } - - /** Gets the `i`th name declared by this specifier (0-based). */ - string getName(int i) { result = this.getNameExpr(i).getName() } - - /** Gets a name declared by this specifier. */ - string getAName() { result = this.getName(_) } - - /** Gets the number of names declared by this specifier. */ - int getNumName() { result = count(this.getANameExpr()) } - - /** Gets the expression denoting the type of the symbols declared by this specifier. */ - Expr getTypeExpr() { result = this.getChildExpr(0) } - - /** Gets the `i`th initializer of this specifier (0-based). */ - Expr getInit(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** Gets an initializer of this specifier. */ - Expr getAnInit() { result = this.getInit(_) } - - /** Gets the number of initializers of this specifier. */ - int getNumInit() { result = count(this.getAnInit()) } - - /** Gets the unique initializer of this specifier, if there is only one. */ - Expr getInit() { this.getNumInit() = 1 and result = this.getInit(0) } - - /** - * Gets the specifier that contains the initializers for this specifier. - * If this valuespec has initializers, the result is itself. Otherwise, it is the - * last specifier declared before this one that has initializers. - */ - private ValueSpec getEffectiveSpec() { - (exists(this.getAnInit()) or not this.isConstSpec()) and - result = this - or - not exists(this.getAnInit()) and - exists(ConstDecl decl, int idx | - decl = this.getParentDecl() and - decl.getSpec(idx) = this - | - result = decl.getSpec(idx - 1).(ValueSpec).getEffectiveSpec() - ) - } - - /** - * Gets the `i`th effective initializer of this specifier, that is, the expression - * that the `i`th name will get initialized to. This is the same as `getInit` - * if it exists, or `getInit` on the last specifier in the declaration that this - * is a child of. - */ - private Expr getEffectiveInit(int i) { result = this.getEffectiveSpec().getInit(i) } - - /** Holds if this specifier initializes `name` to the value of `init`. */ - predicate initializes(string name, Expr init) { - exists(int i | - name = this.getName(i) and - init = this.getEffectiveInit(i) - ) - } - - override predicate mayHaveSideEffects() { this.getAnInit().mayHaveSideEffects() } - - override string toString() { result = "value declaration specifier" } - - override string getAPrimaryQlClass() { result = "ValueSpec" } -} - -/** - * A type declaration specifier, which is either a type definition or an alias declaration. - * - * Examples: - * - * ``` - * type ( - * status int - * intlist = []int - * ) - * ``` - */ -class TypeSpec extends @typespec, Spec, TypeParamDeclParent { - /** Gets the identifier denoting the name of the declared type. */ - Ident getNameExpr() { result = this.getChildExpr(0) } - - /** Gets the name of the declared type. */ - string getName() { result = this.getNameExpr().getName() } - - /** - * Gets the expression denoting the underlying type to which the newly declared type is bound. - */ - Expr getTypeExpr() { result = this.getChildExpr(1) } - - override string toString() { result = "type declaration specifier" } - - override string getAPrimaryQlClass() { result = "TypeSpec" } -} - -/** - * An alias declaration specifier. - * - * Examples: - * - * ``` - * type intlist = []int - * ``` - */ -class AliasSpec extends @aliasspec, TypeSpec { } - -/** - * A type definition specifier. - * - * Examples: - * - * ``` - * type status int - * ``` - */ -class TypeDefSpec extends @typedefspec, TypeSpec { } - -/** - * A field declaration, of a struct, a function (in which case this is a parameter or result variable), - * or an interface (in which case this is a method or embedding spec). - */ -class FieldBase extends @field, ExprParent { - /** - * Gets the expression representing the type of the fields declared in this declaration. - */ - Expr getTypeExpr() { result = this.getChildExpr(0) } - - /** - * Gets the type of the fields declared in this declaration. - */ - Type getType() { result = this.getTypeExpr().getType() } -} - -/** - * A field declaration in a struct type. - */ -class FieldDecl extends FieldBase, Documentable, ExprParent { - StructTypeExpr st; - - FieldDecl() { this = st.getField(_) } - - /** - * Gets the expression representing the name of the `i`th field declared in this declaration - * (0-based). - */ - Expr getNameExpr(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** Gets the tag expression of this field declaration, if any. */ - Expr getTag() { result = this.getChildExpr(-1) } - - /** Gets the struct type expression to which this field declaration belongs. */ - StructTypeExpr getDeclaringStructTypeExpr() { result = st } - - /** Gets the struct type to which this field declaration belongs. */ - StructType getDeclaringType() { result = this.getDeclaringStructTypeExpr().getType() } - - override string toString() { result = "field declaration" } - - override string getAPrimaryQlClass() { result = "FieldDecl" } -} - -/** - * An embedded field declaration in a struct. - */ -class EmbeddedFieldDecl extends FieldDecl { - EmbeddedFieldDecl() { not exists(this.getNameExpr(_)) } - - override string getAPrimaryQlClass() { result = "EmbeddedFieldDecl" } -} - -/** - * A function parameter or result variable declaration. - */ -class ParameterOrResultDecl extends FieldBase, Documentable, ExprParent { - int rawIndex; - FuncTypeExpr ft; - - ParameterOrResultDecl() { this = ft.getField(rawIndex) } - - /** - * Gets the function type expression to which this declaration belongs. - */ - FuncTypeExpr getFunctionTypeExpr() { result = ft } - - /** - * Gets the function to which this declaration belongs. - */ - FuncDef getFunction() { result.getTypeExpr() = this.getFunctionTypeExpr() } - - /** - * Gets the expression representing the name of the `i`th variable declared in this declaration - * (0-based). - */ - Expr getNameExpr(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** - * Gets an expression representing the name of a variable declared in this declaration. - */ - Expr getANameExpr() { result = this.getNameExpr(_) } -} - -/** - * A parameter declaration. - */ -class ParameterDecl extends ParameterOrResultDecl { - ParameterDecl() { rawIndex >= 0 } - - /** - * Gets the index of this parameter declarations among all parameter declarations of - * its associated function type. - */ - int getIndex() { result = rawIndex } - - override string toString() { result = "parameter declaration" } - - override string getAPrimaryQlClass() { result = "ParameterDecl" } -} - -/** - * A receiver declaration in a function declaration. - */ -class ReceiverDecl extends FieldBase, Documentable, ExprParent { - FuncDecl fd; - - ReceiverDecl() { fd.getField(-1) = this } - - /** - * Gets the function declaration to which this receiver belongs. - */ - FuncDecl getFunction() { result = fd } - - /** - * Gets the expression representing the name of the receiver declared in this declaration. - */ - Expr getNameExpr() { result = this.getChildExpr(1) } - - override string toString() { result = "receiver declaration" } - - override string getAPrimaryQlClass() { result = "ReceiverDecl" } -} - -/** - * A result variable declaration. - */ -class ResultVariableDecl extends ParameterOrResultDecl { - ResultVariableDecl() { rawIndex < 0 } - - /** - * Gets the index of this result variable declaration among all result variable declarations of - * its associated function type. - */ - int getIndex() { result = -(rawIndex + 1) } - - override string toString() { result = "result variable declaration" } - - override string getAPrimaryQlClass() { result = "ResultVariableDecl" } -} - -/** - * A type parameter declaration in a type specification. - */ -class TypeParamDecl extends @typeparamdecl, Documentable, ExprParent { - TypeParamDecl() { typeparamdecls(this, _, _) } - - /** - * Gets the expression representing the type constraint of the type - * parameters in this declaration. - * - * If you want the type constraint type itself then use `getTypeConstraint`, - * as that wraps type set literals with implicit interface types. - */ - Expr getTypeConstraintExpr() { result = this.getChildExpr(0) } - - /** - * Gets the type constraint of the type parameters in this declaration. - * - * If the type constraint is a type set literal then it will be wrapped - * with an implicit interface type. - */ - Type getTypeConstraint() { - exists(Type t | t = this.getTypeConstraintExpr().getType() | - if t instanceof TypeSetLiteralType - then result = t.(TypeSetLiteralType).getInterfaceType() - else result = t - ) - } - - /** - * Gets the expression representing the name of the `i`th type parameter - * in this declaration (0-based). - */ - Expr getNameExpr(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** - * Gets the `i`th type parameter type in this declaration (0-based). - */ - TypeParamType getTypeParamType(int i) { - i >= 0 and - result = this.getNameExpr(i).getType() - } - - override string toString() { result = "type parameter declaration" } - - override string getAPrimaryQlClass() { result = "TypeParamDecl" } -} - -/** - * A method or embedding specification in an interface type expression. - */ -class InterfaceMemberSpec extends FieldBase, Documentable, ExprParent { - InterfaceTypeExpr ite; - int idx; - - InterfaceMemberSpec() { this = ite.getField(idx) } - - /** - * Gets the interface type expression to which this member specification belongs. - */ - InterfaceTypeExpr getInterfaceTypeExpr() { result = ite } - - /** - * Gets the index of this member specification among all member specifications of - * its associated interface type expression. - */ - int getIndex() { result = idx } -} - -/** - * A method specification in an interface. - */ -class MethodSpec extends InterfaceMemberSpec { - Expr name; - - MethodSpec() { name = this.getChildExpr(1) } - - /** - * Gets the expression representing the name of the method declared in this specification. - */ - Expr getNameExpr() { result = name } - - override string toString() { result = "method declaration" } - - override string getAPrimaryQlClass() { result = "MethodSpec" } -} - -/** - * An embedding specification in an interface. - */ -class EmbeddingSpec extends InterfaceMemberSpec { - EmbeddingSpec() { not exists(this.getChildExpr(1)) } - - override string toString() { result = "interface embedding" } - - override string getAPrimaryQlClass() { result = "EmbeddingSpec" } -} diff --git a/ql/lib/semmle/go/Errors.qll b/ql/lib/semmle/go/Errors.qll deleted file mode 100644 index cf83a87ff..000000000 --- a/ql/lib/semmle/go/Errors.qll +++ /dev/null @@ -1,53 +0,0 @@ -/** Provides classes for working with Go frontend errors recorded during extraction. */ - -import go - -/** - * An error reported by the Go frontend during extraction. - */ -class Error extends @error { - /** Gets the message associated with this error. */ - string getMessage() { errors(this, _, result, _, _, _, _, _, _) } - - /** Gets the raw position reported by the frontend for this error. */ - string getRawPosition() { errors(this, _, _, result, _, _, _, _, _) } - - /** Gets the package in which this error was reported. */ - Package getPackage() { errors(this, _, _, _, _, _, _, result, _) } - - /** Gets the index of this error among all errors reported for the same package. */ - int getIndex() { errors(this, _, _, _, _, _, _, _, result) } - - /** Gets the file in which this error was reported, if it can be determined. */ - ExtractedOrExternalFile getFile() { hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [LGTM locations](https://lgtm.com/help/ql/locations). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - errors(this, _, _, _, filepath, startline, startcolumn, _, _) and - endline = startline and - endcolumn = startcolumn - } - - /** Gets a textual representation of this error. */ - string toString() { result = getMessage() } -} - -/** An error reported by an unknown part of the Go frontend. */ -class UnknownError extends Error, @unknownerror { } - -/** An error reported by the Go frontend driver. */ -class ListError extends Error, @listerror { } - -/** An error reported by the Go parser. */ -class ParseError extends Error, @parseerror { } - -/** An error reported by the Go type checker. */ -class TypeError extends Error, @typeerror { } diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll deleted file mode 100644 index d32d87287..000000000 --- a/ql/lib/semmle/go/Expr.qll +++ /dev/null @@ -1,2234 +0,0 @@ -/** - * Provides classes for working with expressions. - */ - -import go - -/** - * An expression. - * - * Examples: - * - * ```go - * x + 1 - * y < 0 - * ``` - */ -class Expr extends @expr, ExprParent { - /** - * Gets the kind of this expression, which is an integer value representing the expression's - * node type. - * - * Note that the mapping from node types to integer kinds is considered an implementation detail - * and subject to change without notice. - */ - int getKind() { exprs(this, result, _, _) } - - /** Gets this expression, with any surrounding parentheses removed. */ - Expr stripParens() { result = this } - - /** - * Holds if this expression is constant, that is, if its value is determined at - * compile-time. - */ - predicate isConst() { constvalues(this, _, _) } - - /** - * Gets the boolean value this expression evalutes to, if any. - */ - boolean getBoolValue() { - this.getType().getUnderlyingType() instanceof BoolType and - exists(string val | constvalues(this, val, _) | - val = "true" and result = true - or - val = "false" and result = false - ) - } - - /** Gets the floating-point value this expression evaluates to, if any. */ - float getFloatValue() { - this.getType().getUnderlyingType() instanceof FloatType and - exists(string val | constvalues(this, val, _) | result = val.toFloat()) - } - - /** - * Gets the integer value this expression evaluates to, if any. - * - * Note that this does not have a result if the value is too large to fit in a - * 32-bit signed integer type. - */ - int getIntValue() { - this.getType().getUnderlyingType() instanceof IntegerType and - exists(string val | constvalues(this, val, _) | result = val.toInt()) - } - - /** Gets either `getFloatValue` or `getIntValue`. */ - float getNumericValue() { result = this.getFloatValue() or result = this.getIntValue() } - - /** - * Holds if the complex value this expression evaluates to has real part `real` and imaginary - * part `imag`. - */ - predicate hasComplexValue(float real, float imag) { - this.getType().getUnderlyingType() instanceof ComplexType and - exists(string val | constvalues(this, val, _) | - exists(string cmplxre | - cmplxre = "^\\((.+) \\+ (.+)i\\)$" and - real = val.regexpCapture(cmplxre, 1).toFloat() and - imag = val.regexpCapture(cmplxre, 2).toFloat() - ) - ) - } - - /** Gets the string value this expression evaluates to, if any. */ - string getStringValue() { - this.getType().getUnderlyingType() instanceof StringType and - constvalues(this, result, _) - } - - /** - * Gets the string representation of the exact value this expression - * evaluates to, if any. - * - * For example, for the constant 3.141592653589793238462, this will - * result in 1570796326794896619231/500000000000000000000 - */ - string getExactValue() { constvalues(this, _, result) } - - /** - * Holds if this expression has a constant value which is guaranteed not to depend on the - * platform where it is evaluated. - * - * This is a conservative approximation, that is, the predicate may fail to hold for expressions - * whose value is platform independent, but it will never hold for expressions whose value is not - * platform independent. - * - * Examples of platform-dependent constants include constants declared in files with build - * constraints, the value of `runtime.GOOS`, and the return value of `unsafe.Sizeof`. - */ - predicate isPlatformIndependentConstant() { none() } - - /** Gets the type of this expression. */ - Type getType() { - type_of(this, result) - or - not type_of(this, _) and - result instanceof InvalidType - } - - /** - * Gets the global value number of this expression. - * - * Expressions with the same global value number are guaranteed to have the same value at runtime. - * The converse does not hold in general, that is, expressions with different global value numbers - * may still have the same value at runtime. - */ - GVN getGlobalValueNumber() { result = globalValueNumber(DataFlow::exprNode(this)) } - - /** - * Holds if this expression may have observable side effects of its own (that is, independent - * of whether its sub-expressions may have side effects). - * - * Memory allocation is not considered an observable side effect. - */ - predicate mayHaveOwnSideEffects() { none() } - - /** - * Holds if the evaluation of this expression may produce observable side effects. - * - * Memory allocation is not considered an observable side effect. - */ - predicate mayHaveSideEffects() { - this.mayHaveOwnSideEffects() or this.getAChildExpr().mayHaveSideEffects() - } - - override string toString() { result = "expression" } -} - -/** - * A bad expression, that is, an expression that could not be parsed. - * - * Examples: - * - * ```go - * x + - * y < - * ``` - */ -class BadExpr extends @badexpr, Expr { - override string toString() { result = "bad expression" } - - override string getAPrimaryQlClass() { result = "BadExpr" } -} - -/** - * An identifier. - * - * Examples: - * - * ```go - * x - * ``` - */ -class Ident extends @ident, Expr { - /** Gets the name of this identifier. */ - string getName() { literals(this, result, _) } - - /** Holds if this identifier is a use of `e`. */ - predicate uses(Entity e) { uses(this, e) } - - /** Holds if this identifier is a definition or declaration of `e` */ - predicate declares(Entity e) { defs(this, e) } - - /** Holds if this identifier refers to (that is, uses, defines or declares) `e`. */ - predicate refersTo(Entity e) { this.uses(e) or this.declares(e) } - - override string toString() { result = this.getName() } - - override string getAPrimaryQlClass() { result = "Ident" } -} - -/** - * The blank identifier `_`. - * - * Examples: - * - * ```go - * _ - * ``` - */ -class BlankIdent extends Ident { - BlankIdent() { this.getName() = "_" } - - override string getAPrimaryQlClass() { result = "BlankIdent" } -} - -/** - * An ellipsis expression, representing either the `...` type in a parameter list or - * the `...` length in an array type. - * - * Examples: - * - * ```go - * ... - * ``` - */ -class Ellipsis extends @ellipsis, Expr { - /** Gets the operand of this ellipsis expression. */ - Expr getOperand() { result = this.getChildExpr(0) } - - override string toString() { result = "..." } - - override string getAPrimaryQlClass() { result = "Ellipsis" } -} - -/** - * A literal expression. - * - * Examples: - * - * ```go - * "hello" - * func(x, y int) int { return x + y } - * map[string]int{"A": 1, "B": 2} - * ``` - */ -class Literal extends Expr { - Literal() { - this instanceof @basiclit or this instanceof @funclit or this instanceof @compositelit - } -} - -/** - * A literal expression of basic type. - * - * Examples: - * - * ```go - * 1 - * "hello" - * ``` - */ -class BasicLit extends @basiclit, Literal { - /** Gets the value of this literal expressed as a string. */ - string getValue() { literals(this, result, _) } - - /** Gets the raw program text corresponding to this literal. */ - string getText() { literals(this, _, result) } - - override predicate isConst() { - // override to make sure literals are always considered constants even if we did not get - // information about constant values from the extractor (for example due to missing - // type information) - any() - } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = this.getText() } -} - -/** - * An integer literal. - * - * Examples: - * - * ```go - * 256 - * ``` - */ -class IntLit extends @intlit, BasicLit { - override string getAPrimaryQlClass() { result = "IntLit" } -} - -/** - * A floating-point literal. - * - * Examples: - * - * ```go - * 2.71828 - * ``` - */ -class FloatLit extends @floatlit, BasicLit { - override string getAPrimaryQlClass() { result = "FloatLit" } -} - -/** - * An imaginary literal. - * - * Examples: - * - * ```go - * 2i - * 2.7i - * ``` - */ -class ImagLit extends @imaglit, BasicLit { - override string getAPrimaryQlClass() { result = "ImagLit" } -} - -/** - * A rune literal. - * - * Examples: - * - * ```go - * 'a' - * 'ä' - * '本' - * '\377' - * '\xff' - * '\u12e4' - * '\U00101234' - * '\n' - * ``` - */ -class CharLit extends @charlit, BasicLit { - // use the constant value of the literal as the string value, as the value we get from the - // compiler is an integer, meaning we would not otherwise have a string value for rune literals - override string getStringValue() { result = this.getValue() } - - override string getAPrimaryQlClass() { result = "CharLit" } -} - -class RuneLit = CharLit; - -/** - * A string literal. - * - * Examples: - * - * ```go - * "hello world" - * ``` - */ -class StringLit extends @stringlit, BasicLit { - override string getAPrimaryQlClass() { result = "StringLit" } - - /** Holds if this string literal is a raw string literal. */ - predicate isRaw() { this.getText().matches("`%`") } -} - -/** - * A function literal. - * - * Examples: - * - * ```go - * func(x, y int) int { return x + y } - * ``` - */ -class FuncLit extends @funclit, Literal, StmtParent, FuncDef { - override FuncTypeExpr getTypeExpr() { result = this.getChildExpr(0) } - - override SignatureType getType() { result = Literal.super.getType() } - - /** Gets the body of this function literal. */ - override BlockStmt getBody() { result = this.getChildStmt(1) } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "function literal" } - - override string getAPrimaryQlClass() { result = "FuncLit" } -} - -/** - * A composite literal - * - * Examples: - * - * ```go - * Point3D{0.5, -0.5, 0.5} - * map[string]int{"A": 1, "B": 2} - * ``` - */ -class CompositeLit extends @compositelit, Literal { - /** Gets the expression representing the type of this composite literal. */ - Expr getTypeExpr() { result = this.getChildExpr(0) } - - /** Gets the `i`th element of this composite literal (0-based). */ - Expr getElement(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** Gets an element of this composite literal. */ - Expr getAnElement() { result = this.getElement(_) } - - /** Gets the number of elements in this composite literal. */ - int getNumElement() { result = count(this.getAnElement()) } - - /** - * Gets the `i`th key expression in this literal. - * - * If the `i`th element of this literal has no key, this predicate is undefined for `i`. - */ - Expr getKey(int i) { result = this.getElement(i).(KeyValueExpr).getKey() } - - /** - * Gets the `i`th value expression in this literal. - */ - Expr getValue(int i) { - exists(Expr elt | elt = this.getElement(i) | - result = elt.(KeyValueExpr).getValue() - or - not elt instanceof KeyValueExpr and result = elt - ) - } - - override string toString() { result = "composite literal" } - - override string getAPrimaryQlClass() { result = "CompositeLit" } -} - -/** - * A map literal. - * - * Examples: - * - * ```go - * map[string]int{"A": 1, "B": 2} - * ``` - */ -class MapLit extends CompositeLit { - MapType mt; - - MapLit() { mt = this.getType().getUnderlyingType() } - - /** Gets the key type of this literal. */ - Type getKeyType() { result = mt.getKeyType() } - - /** Gets the value type of this literal. */ - Type getValueType() { result = mt.getValueType() } - - override string toString() { result = "map literal" } - - override string getAPrimaryQlClass() { result = "MapLit" } -} - -/** - * A struct literal. - * - * Examples: - * - * ```go - * Point3D{0.5, -0.5, 0.5} - * Point3D{y: 1} - * Point3D{} - * ``` - */ -class StructLit extends CompositeLit { - StructType st; - - StructLit() { st = this.getType().getUnderlyingType() } - - /** Gets the struct type underlying this literal. */ - StructType getStructType() { result = st } - - override string toString() { result = "struct literal" } - - override string getAPrimaryQlClass() { result = "StructLit" } -} - -/** - * An array or slice literal. - * - * Examples: - * - * ```go - * [10]string{} - * [6]int{1, 2, 3, 5} - * [...]string{"Sat", "Sun"} - * []int{1, 2, 3, 5} - * []string{"Sat", "Sun"} - * ``` - */ -class ArrayOrSliceLit extends CompositeLit { - CompositeType type; - - ArrayOrSliceLit() { - type = this.getType().getUnderlyingType() and - ( - type instanceof ArrayType - or - type instanceof SliceType - ) - } -} - -/** - * An array literal. - * - * Examples: - * - * ```go - * [10]string{} - * [6]int{1, 2, 3, 5} - * [...]string{"Sat", "Sun"} - * ``` - */ -class ArrayLit extends ArrayOrSliceLit { - override ArrayType type; - - /** Gets the array type underlying this literal. */ - ArrayType getArrayType() { result = type } - - override string toString() { result = "array literal" } - - override string getAPrimaryQlClass() { result = "ArrayLit" } -} - -/** - * A slice literal. - * - * Examples: - * - * ```go - * []int{1, 2, 3, 5} - * []string{"Sat", "Sun"} - * ``` - */ -class SliceLit extends ArrayOrSliceLit { - override SliceType type; - - /** Gets the slice type underlying this literal. */ - SliceType getSliceType() { result = type } - - override string toString() { result = "slice literal" } - - override string getAPrimaryQlClass() { result = "SliceLit" } -} - -/** - * A parenthesized expression. - * - * Examples: - * - * ```go - * (x + y) - * ``` - */ -class ParenExpr extends @parenexpr, Expr { - /** Gets the expression between parentheses. */ - Expr getExpr() { result = this.getChildExpr(0) } - - override Expr stripParens() { result = this.getExpr().stripParens() } - - override predicate isPlatformIndependentConstant() { - this.getExpr().isPlatformIndependentConstant() - } - - override string toString() { result = "(...)" } - - override string getAPrimaryQlClass() { result = "ParenExpr" } -} - -/** - * A selector expression, that is, a base expression followed by a selector. - * - * Examples: - * - * ```go - * x.f - * ``` - */ -class SelectorExpr extends @selectorexpr, Expr { - /** Gets the base of this selector expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - /** Gets the selector of this selector expression. */ - Ident getSelector() { result = this.getChildExpr(1) } - - /** Holds if this selector is a use of `e`. */ - predicate uses(Entity e) { this.getSelector().uses(e) } - - /** Holds if this selector is a definition of `e` */ - predicate declares(Entity e) { this.getSelector().declares(e) } - - /** Holds if this selector refers to (that is, uses, defines or declares) `e`. */ - predicate refersTo(Entity e) { this.getSelector().refersTo(e) } - - override predicate mayHaveOwnSideEffects() { any() } - - override string toString() { result = "selection of " + this.getSelector() } - - override string getAPrimaryQlClass() { result = "SelectorExpr" } -} - -/** - * A selector expression that refers to a promoted field or a promoted method. These - * selectors may implicitly address an embedded struct of their base type - for example, - * the selector `x.field` may implicitly address `x.Embedded.field`). Note they may also - * explicitly address `field`; being a `PromotedSelector` only indicates the addressed - * field or method may be promoted, not that it is promoted in this particular context. - */ -class PromotedSelector extends SelectorExpr { - PromotedSelector() { - exists(ValueEntity ve | this.refersTo(ve) | - ve instanceof PromotedField or ve instanceof PromotedMethod - ) - } - - /** - * Gets the underlying struct type of this selector's base. Note because this selector - * addresses a promoted field, the addressed field may not directly occur in the returned - * struct type. - */ - StructType getSelectedStructType() { - exists(Type baseType | baseType = this.getBase().getType().getUnderlyingType() | - pragma[only_bind_into](result) = - [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] - ) - } -} - -/** - * An index expression, that is, a base expression followed by an index. - * Expressions which represent generic type instantiations have been - * excluded. - * - * Examples: - * - * ```go - * array[i] - * arrayptr[i] - * slice[i] - * map[key] - * ``` - */ -class IndexExpr extends @indexexpr, Expr { - IndexExpr() { not isTypeExprBottomUp(this.getChildExpr(0)) } - - /** Gets the base of this index expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - /** Gets the index of this index expression. */ - Expr getIndex() { result = this.getChildExpr(1) } - - override predicate mayHaveOwnSideEffects() { any() } - - override string toString() { result = "index expression" } - - override string getAPrimaryQlClass() { result = "IndexExpr" } -} - -/** - * A generic function instantiation, that is, a base expression that represents - * a generic function, followed by a list of type arguments. - * - * Examples: - * - * ```go - * genericfunction[type] - * genericfunction[type1, type2] - * ``` - */ -class GenericFunctionInstantiationExpr extends @genericfunctioninstantiationexpr, Expr { - /** Gets the generic function expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - /** Gets the `i`th type argument. */ - Expr getTypeArgument(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - override predicate mayHaveOwnSideEffects() { any() } - - override string toString() { result = "generic function instantiation expression" } - - override string getAPrimaryQlClass() { result = "GenericFunctionInstantiationExpr" } -} - -/** - * A generic type instantiation, that is, a base expression that is a generic - * type followed by a list of type arguments. - * - * Examples: - * - * ```go - * generictype[type] - * generictype[type1, type2] - * ``` - */ -class GenericTypeInstantiationExpr extends Expr { - GenericTypeInstantiationExpr() { - this instanceof @generictypeinstantiationexpr - or - this instanceof @indexexpr and isTypeExprBottomUp(this.getChildExpr(0)) - } - - /** Gets the generic type expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - /** Gets the `i`th type argument. */ - Expr getTypeArgument(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - override predicate mayHaveOwnSideEffects() { any() } - - override string toString() { result = "generic type instantiation expression" } - - override string getAPrimaryQlClass() { result = "GenericTypeInstantiationExpr" } -} - -/** - * A slice expression, that is, a base expression followed by slice indices. - * - * Examples: - * - * ```go - * a[1:3] - * a[1:3:5] - * ``` - */ -class SliceExpr extends @sliceexpr, Expr { - /** Gets the base of this slice expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - /** Gets the lower bound of this slice expression. */ - Expr getLow() { result = this.getChildExpr(1) } - - /** Gets the upper bound of this slice expression. */ - Expr getHigh() { result = this.getChildExpr(2) } - - /** Gets the maximum of this slice expression, if any. */ - Expr getMax() { result = this.getChildExpr(3) } - - override string toString() { result = "slice expression" } - - override string getAPrimaryQlClass() { result = "SliceExpr" } -} - -/** - * A type assertion expression. - * - * Examples: - * - * ```go - * x.(T) - * ``` - */ -class TypeAssertExpr extends @typeassertexpr, Expr { - /** Gets the base expression whose type is being asserted. */ - Expr getExpr() { result = this.getChildExpr(0) } - - /** Gets the expression representing the asserted type. */ - Expr getTypeExpr() { result = this.getChildExpr(1) } - - override predicate mayHaveOwnSideEffects() { any() } - - override predicate isPlatformIndependentConstant() { - this.getExpr().isPlatformIndependentConstant() - } - - override string toString() { result = "type assertion" } - - override string getAPrimaryQlClass() { result = "TypeAssertExpr" } -} - -/** - * An expression that syntactically could either be a function call or a type - * conversion expression. - * - * In most cases, the subclasses `CallExpr` and `ConversionExpr` should be used - * instead. - * - * Examples: - * - * ```go - * f(x) - * g(a, b...) - * []byte("x") - * ``` - */ -class CallOrConversionExpr extends @callorconversionexpr, Expr { - override string getAPrimaryQlClass() { result = "CallOrConversionExpr" } -} - -/** - * A type conversion expression. - * - * Examples: - * - * ```go - * []byte("x") - * ``` - */ -class ConversionExpr extends CallOrConversionExpr { - ConversionExpr() { isTypeExprBottomUp(this.getChildExpr(0)) } - - /** Gets the type expression representing the target type of the conversion. */ - Expr getTypeExpr() { result = this.getChildExpr(0) } - - /** Gets the operand of the type conversion. */ - Expr getOperand() { result = this.getChildExpr(1) } - - override predicate isPlatformIndependentConstant() { - this.getOperand().isPlatformIndependentConstant() - } - - override string toString() { result = "type conversion" } - - override string getAPrimaryQlClass() { result = "ConversionExpr" } -} - -/** - * A function call expression. - * - * On snapshots with incomplete type information, type conversions may be misclassified - * as function call expressions. - * - * Examples: - * - * ```go - * f(x) - * g(a, b...) - * ``` - */ -class CallExpr extends CallOrConversionExpr { - CallExpr() { - exists(Expr callee | callee = this.getChildExpr(0) | not isTypeExprBottomUp(callee)) - or - // only calls can have an ellipsis after their last argument - has_ellipsis(this) - } - - /** Gets the expression representing the function being called. */ - Expr getCalleeExpr() { - if this.getChildExpr(0) instanceof GenericFunctionInstantiationExpr - then result = this.getChildExpr(0).(GenericFunctionInstantiationExpr).getBase() - else result = this.getChildExpr(0) - } - - /** Gets the `i`th argument expression of this call (0-based). */ - Expr getArgument(int i) { - i >= 0 and - result = this.getChildExpr(i + 1) - } - - /** Gets an argument expression of this call. */ - Expr getAnArgument() { result = this.getArgument(_) } - - /** Gets the number of argument expressions of this call. */ - int getNumArgument() { result = count(this.getAnArgument()) } - - /** - * Gets the name of the invoked function, method or variable if it can be - * determined syntactically. - * - * Note that if a variable is being called then this gets the variable name - * rather than the name of the function or method that has been assigned to - * the variable. - */ - string getCalleeName() { - exists(Expr callee | callee = this.getCalleeExpr().stripParens() | - result = callee.(Ident).getName() - or - result = callee.(SelectorExpr).getSelector().getName() - ) - } - - /** Gets the declared target of this call. */ - Function getTarget() { this.getCalleeExpr() = result.getAReference() } - - /** Holds if this call has an ellipsis after its last argument. */ - predicate hasEllipsis() { has_ellipsis(this) } - - override predicate mayHaveOwnSideEffects() { - this.getTarget().mayHaveSideEffects() or - not exists(this.getTarget()) - } - - override string toString() { - result = "call to " + this.getCalleeName() - or - not exists(this.getCalleeName()) and - result = "function call" - } - - override string getAPrimaryQlClass() { result = "CallExpr" } -} - -/** - * A star expression. - * - * Examples: - * - * ```go - * *x - * ``` - */ -class StarExpr extends @starexpr, Expr { - /** Gets the base expression of this star expression. */ - Expr getBase() { result = this.getChildExpr(0) } - - override predicate mayHaveOwnSideEffects() { any() } - - override string toString() { result = "star expression" } - - override string getAPrimaryQlClass() { result = "StarExpr" } -} - -/** - * A key-value pair in a composite literal. - * - * Examples: - * - * ```go - * "A": 1 - * ``` - */ -class KeyValueExpr extends @keyvalueexpr, Expr { - /** Gets the key expression of this key-value pair. */ - Expr getKey() { result = this.getChildExpr(0) } - - /** Gets the value expression of this key-value pair. */ - Expr getValue() { result = this.getChildExpr(1) } - - /** Gets the composite literal to which this key-value pair belongs. */ - CompositeLit getLiteral() { this = result.getElement(_) } - - override string toString() { result = "key-value pair" } - - override string getAPrimaryQlClass() { result = "KeyValueExpr" } -} - -/** - * An expression representing an array type. - * - * Examples: - * - * ```go - * [5]int - * ``` - */ -class ArrayTypeExpr extends @arraytypeexpr, TypeExpr { - /** Gets the length expression of this array type. */ - Expr getLength() { result = this.getChildExpr(0) } - - /** Gets the expression representing the element type of this array type. */ - Expr getElement() { result = this.getChildExpr(1) } - - override string toString() { result = "array type" } - - override string getAPrimaryQlClass() { result = "ArrayTypeExpr" } -} - -/** - * An expression representing a struct type. - * - * Examples: - * - * ```go - * struct {x, y int; z float32} - * ``` - */ -class StructTypeExpr extends @structtypeexpr, TypeExpr, FieldParent { - override string toString() { result = "struct type" } - - override string getAPrimaryQlClass() { result = "StructTypeExpr" } -} - -/** - * An expression representing a function type. - * - * Examples: - * - * ```go - * func(a, b int, c float32) (float32, bool) - * ``` - */ -class FuncTypeExpr extends @functypeexpr, TypeExpr, ScopeNode, FieldParent { - /** Gets the `i`th parameter of this function type (0-based). */ - ParameterDecl getParameterDecl(int i) { result = this.getField(i) and i >= 0 } - - /** Gets a parameter of this function type. */ - ParameterDecl getAParameterDecl() { result = this.getParameterDecl(_) } - - /** Gets the number of parameters of this function type. */ - int getNumParameter() { result = count(this.getAParameterDecl()) } - - /** Gets the `i`th result of this function type (0-based). */ - ResultVariableDecl getResultDecl(int i) { result = this.getField(-(i + 1)) } - - /** Gets a result of this function type. */ - ResultVariableDecl getAResultDecl() { result = this.getResultDecl(_) } - - /** Gets the number of results of this function type. */ - int getNumResult() { result = count(this.getAResultDecl()) } - - /** Gets the result of this function type, if there is only one. */ - ResultVariableDecl getResultDecl() { this.getNumResult() = 1 and result = this.getAResultDecl() } - - override string toString() { result = "function type" } - - override string getAPrimaryQlClass() { result = "FuncTypeExpr" } - - /** Gets the `i`th child of this node, parameters first followed by results. */ - override AstNode getUniquelyNumberedChild(int i) { - if i < this.getNumParameter() - then result = this.getParameterDecl(i) - else result = this.getResultDecl(i - this.getNumParameter()) - } -} - -/** - * An expression representing an interface type. - * - * Examples: - * - * ```go - * interface { Read(p []byte) (n int, err error); Close() error} - * ``` - */ -class InterfaceTypeExpr extends @interfacetypeexpr, TypeExpr, FieldParent { - /** Gets the `i`th method specification of this interface type. */ - MethodSpec getMethod(int i) { result = this.getField(i) } - - /** Gets a method of this interface type. */ - MethodSpec getAMethod() { result = this.getMethod(_) } - - /** Gets the number of methods of this interface type. */ - int getNumMethod() { result = count(this.getAMethod()) } - - override string toString() { result = "interface type" } - - override string getAPrimaryQlClass() { result = "InterfaceTypeExpr" } -} - -/** - * An expression representing a map type. - * - * Examples: - * - * ```go - * map[string]int - * ``` - */ -class MapTypeExpr extends @maptypeexpr, TypeExpr { - /** Gets the expression representing the key type of this map type. */ - Expr getKeyTypeExpr() { result = this.getChildExpr(0) } - - /** Gets the key type of this map type. */ - Type getKeyType() { result = this.getKeyTypeExpr().getType() } - - /** Gets the expression representing the value type of this map type. */ - Expr getValueTypeExpr() { result = this.getChildExpr(1) } - - /** Gets the value type of this map type. */ - Type getValueType() { result = this.getValueTypeExpr().getType() } - - override string toString() { result = "map type" } - - override string getAPrimaryQlClass() { result = "MapTypeExpr" } -} - -/** - * An expression representing a type set literal. - * - * Examples: - * - * ```go - * ~string - * int64 | float64 - * ``` - */ -class TypeSetLiteralExpr extends @typesetliteralexpr, TypeExpr { - override string toString() { result = "type set literal" } - - override string getAPrimaryQlClass() { result = "TypeSetLiteralExpr" } -} - -/** - * An expression with a (unary or binary) operator. - * - * Examples: - * - * ```go - * a * b - * -c - * ``` - */ -class OperatorExpr extends @operatorexpr, Expr { - /** Gets the operator of this expression. */ - string getOperator() { none() } - - /** Gets an operand of this expression. */ - Expr getAnOperand() { none() } -} - -/** - * An expression with an arithmetic operator like `-` or `/`. - * - * Examples: - * - * ```go - * x - y - * u / v - * ``` - */ -class ArithmeticExpr extends @arithmeticexpr, OperatorExpr { } - -/** - * An expression with a logical operator like `!` or `&&`. - * - * Examples: - * - * ```go - * !a - * b && c - * ``` - */ -class LogicalExpr extends @logicalexpr, OperatorExpr { } - -/** - * An expression with a bitwise operator such as `^` or `|`. - * - * Examples: - * - * ```go - * x ^ y - * a | b - * ``` - */ -class BitwiseExpr extends @bitwiseexpr, OperatorExpr { } - -/** - * An expression with a unary operator. - * - * Examples: - * - * ```go - * +7 - * -2.5i - * !x - * ``` - */ -class UnaryExpr extends @unaryexpr, OperatorExpr { - /** Gets the operand of this unary expression. */ - Expr getOperand() { result = this.getChildExpr(0) } - - override Expr getAnOperand() { result = this.getOperand() } - - override predicate isPlatformIndependentConstant() { - this.getOperand().isPlatformIndependentConstant() - } - - override string toString() { result = this.getOperator() + "..." } -} - -/** - * An expression with a unary arithmetic operator, that is, unary `-` or `+`. - * - * Examples: - * - * ```go - * +7 - * -2.5i - * ``` - */ -class ArithmeticUnaryExpr extends @arithmeticunaryexpr, ArithmeticExpr, UnaryExpr { } - -/** - * An expression with a unary logical operator, that is, `!`. - * - * Examples: - * - * ```go - * !x - * ``` - */ -class LogicalUnaryExpr extends @logicalunaryexpr, LogicalExpr, UnaryExpr { } - -/** - * An expression with a unary bitwise operator, that is, `^`. - * - * Examples: - * - * ```go - * ^x - * ``` - */ -class BitwiseUnaryExpr extends @bitwiseunaryexpr, BitwiseExpr, UnaryExpr { } - -/** - * A unary plus expression using `+`. - * - * Examples: - * - * ```go - * +7 - * ``` - */ -class PlusExpr extends @plusexpr, ArithmeticUnaryExpr { - override string getOperator() { result = "+" } - - override string getAPrimaryQlClass() { result = "PlusExpr" } -} - -/** - * A unary minus expression using `-`. - * - * Examples: - * - * ```go - * -2.5i - * ``` - */ -class MinusExpr extends @minusexpr, ArithmeticUnaryExpr { - override string getOperator() { result = "-" } - - override string getAPrimaryQlClass() { result = "MinusExpr" } -} - -/** - * A unary "not" expression using `!`. - * - * Examples: - * - * ```go - * !x - * ``` - */ -class NotExpr extends @notexpr, LogicalUnaryExpr { - override string getOperator() { result = "!" } - - override string getAPrimaryQlClass() { result = "NotExpr" } -} - -/** - * A unary complement expression using `^`. - * - * Examples: - * - * ```go - * ^x - * ``` - */ -class ComplementExpr extends @complementexpr, BitwiseUnaryExpr { - override string getOperator() { result = "^" } - - override string getAPrimaryQlClass() { result = "ComplementExpr" } -} - -/** - * A unary pointer-dereference expression. - * - * This class exists for compatibility reasons only and should not normally be used directly. Use `StarExpr` instead. - */ -class DerefExpr extends @derefexpr, UnaryExpr { - override predicate mayHaveOwnSideEffects() { any() } - - override string getOperator() { result = "*" } - - override string getAPrimaryQlClass() { result = "DerefExpr" } -} - -/** - * A unary address-of expression using `&`. - * - * Examples: - * - * ```go - * &x - * ``` - */ -class AddressExpr extends @addressexpr, UnaryExpr { - override predicate mayHaveOwnSideEffects() { any() } - - override string getOperator() { result = "&" } - - override string getAPrimaryQlClass() { result = "AddressExpr" } -} - -/** - * A unary receive expression using `<-`. - * - * Examples: - * - * ```go - * <-chan - * ``` - */ -class RecvExpr extends @arrowexpr, UnaryExpr { - override predicate mayHaveOwnSideEffects() { any() } - - override string getOperator() { result = "<-" } - - override string getAPrimaryQlClass() { result = "RecvExpr" } -} - -/** - * A binary expression. - * - * Examples: - * - * ```go - * a * b - * a || b - * b != c - * ``` - */ -class BinaryExpr extends @binaryexpr, OperatorExpr { - /** Gets the left operand of this binary expression. */ - Expr getLeftOperand() { result = this.getChildExpr(0) } - - /** Gets the right operand of this binary expression. */ - Expr getRightOperand() { result = this.getChildExpr(1) } - - override Expr getAnOperand() { result = this.getChildExpr([0 .. 1]) } - - /** Holds if `e` and `f` (in either order) are the two operands of this binary expression. */ - predicate hasOperands(Expr e, Expr f) { - e = this.getAnOperand() and - f = this.getAnOperand() and - e != f - } - - override predicate isPlatformIndependentConstant() { - this.getLeftOperand().isPlatformIndependentConstant() and - this.getRightOperand().isPlatformIndependentConstant() - } - - override string toString() { result = "..." + this.getOperator() + "..." } -} - -/** - * A binary arithmetic expression, that is, `+`, `-`, `*`, `/` or `%`. - * - * Examples: - * - * ```go - * a * b - * ``` - */ -class ArithmeticBinaryExpr extends @arithmeticbinaryexpr, ArithmeticExpr, BinaryExpr { } - -/** - * A binary logical expression, that is, `&&` or `||`. - * - * Examples: - * - * ```go - * a || b - * ``` - */ -class LogicalBinaryExpr extends @logicalbinaryexpr, LogicalExpr, BinaryExpr { } - -/** - * A binary bitwise expression, that is, `<<`, `>>`, `|`, `^`, `&` or `&^`. - * - * Examples: - * - * ```go - * a << i - * b ^ c - * ``` - */ -class BitwiseBinaryExpr extends @bitwisebinaryexpr, BitwiseExpr, BinaryExpr { } - -/** - * A shift expression, that is, `<<` or `>>`. - * - * Examples: - * - * ```go - * a << i - * ``` - */ -class ShiftExpr extends @shiftexpr, BitwiseBinaryExpr { } - -/** - * A comparison expression, that is, `==`, `!=`, `<`, `<=`, `>=` or `>`. - * - * Examples: - * - * ```go - * a != b - * c > d - * ``` - */ -class ComparisonExpr extends @comparison, BinaryExpr { } - -/** - * An equality test, that is, `==` or `!=`. - * - * Examples: - * - * ```go - * a != b - * ``` - */ -class EqualityTestExpr extends @equalitytest, ComparisonExpr { - /** Gets the polarity of this equality test, that is, `true` for `==` and `false` for `!=`. */ - boolean getPolarity() { none() } -} - -/** - * A relational comparison, that is, `<`, `<=`, `>=` or `>`. - * - * Examples: - * - * ```go - * c > d - * ``` - */ -class RelationalComparisonExpr extends @relationalcomparison, ComparisonExpr { - /** Holds if this comparison is strict, that is, it implies inequality. */ - predicate isStrict() { none() } - - /** - * Gets the greater operand of this comparison, that is, the right operand for - * a `<` or `<=` comparison, and the left operand for `>=` or `>`. - */ - Expr getGreaterOperand() { none() } - - /** - * Gets the lesser operand of this comparison, that is, the left operand for - * a `<` or `<=` comparison, and the right operand for `>=` or `>`. - */ - Expr getLesserOperand() { none() } -} - -/** - * A logical-or expression using `||`. - * - * Examples: - * - * ```go - * a || b - * ``` - */ -class LorExpr extends @lorexpr, LogicalBinaryExpr { - override string getOperator() { result = "||" } - - override string getAPrimaryQlClass() { result = "LorExpr" } -} - -class LogOrExpr = LorExpr; - -/** - * A logical-and expression using `&&`. - * - * Examples: - * - * ```go - * a && b - * ``` - */ -class LandExpr extends @landexpr, LogicalBinaryExpr { - override string getOperator() { result = "&&" } - - override string getAPrimaryQlClass() { result = "LandExpr" } -} - -class LogAndExpr = LandExpr; - -/** - * An equality test using `==`. - * - * Examples: - * - * ```go - * a == b - * ``` - */ -class EqlExpr extends @eqlexpr, EqualityTestExpr { - override string getOperator() { result = "==" } - - override boolean getPolarity() { result = true } - - override string getAPrimaryQlClass() { result = "EqlExpr" } -} - -class EqExpr = EqlExpr; - -/** - * An inequality test using `!=`. - * - * Examples: - * - * ```go - * a != b - * ``` - */ -class NeqExpr extends @neqexpr, EqualityTestExpr { - override string getOperator() { result = "!=" } - - override boolean getPolarity() { result = false } - - override string getAPrimaryQlClass() { result = "NeqExpr" } -} - -/** - * A less-than test using `<`. - * - * Examples: - * - * ```go - * a < b - * ``` - */ -class LssExpr extends @lssexpr, RelationalComparisonExpr { - override string getOperator() { result = "<" } - - override predicate isStrict() { any() } - - override Expr getLesserOperand() { result = this.getLeftOperand() } - - override Expr getGreaterOperand() { result = this.getRightOperand() } - - override string getAPrimaryQlClass() { result = "LssExpr" } -} - -class LTExpr = LssExpr; - -/** - * A less-than-or-equal test using `<=`. - * - * Examples: - * - * ```go - * a <= b - * ``` - */ -class LeqExpr extends @leqexpr, RelationalComparisonExpr { - override string getOperator() { result = "<=" } - - override Expr getLesserOperand() { result = this.getLeftOperand() } - - override Expr getGreaterOperand() { result = this.getRightOperand() } - - override string getAPrimaryQlClass() { result = "LeqExpr" } -} - -class LEExpr = LeqExpr; - -/** - * A greater-than test using `>`. - * - * Examples: - * - * ```go - * a > b - * ``` - */ -class GtrExpr extends @gtrexpr, RelationalComparisonExpr { - override string getOperator() { result = ">" } - - override predicate isStrict() { any() } - - override Expr getLesserOperand() { result = this.getRightOperand() } - - override Expr getGreaterOperand() { result = this.getLeftOperand() } - - override string getAPrimaryQlClass() { result = "GtrExpr" } -} - -class GTExpr = GtrExpr; - -/** - * A greater-than-or-equal test using `>=`. - * - * Examples: - * - * ```go - * a >= b - * ``` - */ -class GeqExpr extends @geqexpr, RelationalComparisonExpr { - override string getOperator() { result = ">=" } - - override Expr getLesserOperand() { result = this.getRightOperand() } - - override Expr getGreaterOperand() { result = this.getLeftOperand() } - - override string getAPrimaryQlClass() { result = "GeqExpr" } -} - -class GEExpr = GeqExpr; - -/** - * An addition expression using `+`. - * - * Examples: - * - * ```go - * a + b - * ``` - */ -class AddExpr extends @addexpr, ArithmeticBinaryExpr { - override string getOperator() { result = "+" } - - override string getAPrimaryQlClass() { result = "AddExpr" } -} - -/** - * A subtraction expression using `-`. - * - * Examples: - * - * ```go - * a - b - * ``` - */ -class SubExpr extends @subexpr, ArithmeticBinaryExpr { - override string getOperator() { result = "-" } - - override string getAPrimaryQlClass() { result = "SubExpr" } -} - -/** - * A bitwise or expression using `|`. - * - * Examples: - * - * ```go - * a | b - * ``` - */ -class OrExpr extends @orexpr, BitwiseBinaryExpr { - override string getOperator() { result = "|" } - - override string getAPrimaryQlClass() { result = "OrExpr" } -} - -class BitOrExpr = OrExpr; - -/** - * An exclusive-or expression using `^`. - * - * Examples: - * - * ```go - * a ^ b - * ``` - */ -class XorExpr extends @xorexpr, BitwiseBinaryExpr { - override string getOperator() { result = "^" } - - override string getAPrimaryQlClass() { result = "XorExpr" } -} - -/** - * A multiplication expression using `*`. - * - * Examples: - * - * ```go - * a * b - * ``` - */ -class MulExpr extends @mulexpr, ArithmeticBinaryExpr { - override string getOperator() { result = "*" } - - override string getAPrimaryQlClass() { result = "MulExpr" } -} - -/** - * A divison or quotient expression using `/`. - * - * Examples: - * - * ```go - * a / b - * ``` - */ -class QuoExpr extends @quoexpr, ArithmeticBinaryExpr { - override predicate mayHaveOwnSideEffects() { any() } - - override string getOperator() { result = "/" } - - override string getAPrimaryQlClass() { result = "QuoExpr" } -} - -class DivExpr = QuoExpr; - -/** - * A remainder or modulo expression using `%`. - * - * Examples: - * - * ```go - * a % b - * ``` - */ -class RemExpr extends @remexpr, ArithmeticBinaryExpr { - override string getOperator() { result = "%" } - - override string getAPrimaryQlClass() { result = "RemExpr" } -} - -class ModExpr = RemExpr; - -/** - * A left-shift expression using `<<`. - * - * Examples: - * - * ```go - * a << i - * ``` - */ -class ShlExpr extends @shlexpr, ShiftExpr { - override string getOperator() { result = "<<" } - - override string getAPrimaryQlClass() { result = "ShlExpr" } -} - -class LShiftExpr = ShlExpr; - -/** - * A right-shift expression using `>>`. - * - * Examples: - * - * ```go - * a >> i - * ``` - */ -class ShrExpr extends @shrexpr, ShiftExpr { - override string getOperator() { result = ">>" } - - override string getAPrimaryQlClass() { result = "ShrExpr" } -} - -class RShiftExpr = ShrExpr; - -/** - * A bitwise and-expression using `&`. - * - * Examples: - * - * ```go - * a & b - * ``` - */ -class AndExpr extends @andexpr, BitwiseBinaryExpr { - override string getOperator() { result = "&" } - - override string getAPrimaryQlClass() { result = "AndExpr" } -} - -class BitAndExpr = AndExpr; - -/** - * A bitwise and-not expression using `&^`. - * - * Examples: - * - * ```go - * a &^ b - * ``` - */ -class AndNotExpr extends @andnotexpr, BitwiseBinaryExpr { - override string getOperator() { result = "&^" } - - override string getAPrimaryQlClass() { result = "AndNotExpr" } -} - -/** - * An expression representing a channel type. - * - * Examples: - * - * ```go - * chan float64 - * chan<- bool - * <-chan int - * ``` - */ -class ChanTypeExpr extends @chantypeexpr, TypeExpr { - /** - * Gets the expression representing the type of values flowing through the channel. - */ - Expr getValueTypeExpr() { result = this.getChildExpr(0) } - - /** Holds if this channel can send data. */ - predicate canSend() { none() } - - /** Holds if this channel can receive data. */ - predicate canReceive() { none() } - - override string toString() { result = "channel type" } - - override string getAPrimaryQlClass() { result = "ChanTypeExpr" } -} - -/** - * An expression representing a send-only channel type. - * - * Examples: - * - * ```go - * chan<- bool - * ``` - */ -class SendChanTypeExpr extends @sendchantypeexpr, ChanTypeExpr { - override predicate canSend() { any() } - - override string getAPrimaryQlClass() { result = "SendChanTypeExpr" } -} - -/** - * An expression representing a receive-only channel type. - * - * Examples: - * - * ```go - * <-chan int - * ``` - */ -class RecvChanTypeExpr extends @recvchantypeexpr, ChanTypeExpr { - override predicate canReceive() { any() } - - override string getAPrimaryQlClass() { result = "RecvChanTypeExpr" } -} - -/** - * An expression representing a duplex channel type that can both send and receive data. - * - * Examples: - * - * ```go - * chan float64 - * ``` - */ -class SendRecvChanTypeExpr extends @sendrcvchantypeexpr, ChanTypeExpr { - override predicate canSend() { any() } - - override predicate canReceive() { any() } - - override string getAPrimaryQlClass() { result = "SendRecvChanTypeExpr" } -} - -/** - * A (possibly qualified) name referring to a package, type, constant, variable, function or label. - * - * Examples: - * - * ```go - * Println - * fmt.Println - * fmt - * int - * T - * x - * Outerloop - * ``` - */ -class Name extends Expr { - Entity target; - - Name() { this.(Ident).refersTo(target) or this.(SelectorExpr).refersTo(target) } - - /** Gets the entity this name refers to. */ - Entity getTarget() { result = target } -} - -/** - * A simple (that is, unqualified) name. - * - * Examples: - * - * ```go - * Println - * ``` - */ -class SimpleName extends Name, Ident { } - -/** - * A qualified name. - * - * Examples: - * - * ```go - * fmt.Println - * ``` - */ -class QualifiedName extends Name, SelectorExpr { } - -/** - * A name referring to an imported package. - * - * Examples: - * - * ```go - * fmt - * ``` - */ -class PackageName extends Name { - override PackageEntity target; - - /** Gets the package this name refers to. */ - override PackageEntity getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "PackageName" } -} - -/** - * A name referring to a type. - * - * Examples: - * - * ```go - * int - * T - * ``` - */ -class TypeName extends Name { - override TypeEntity target; - - /** Gets the type this name refers to. */ - override TypeEntity getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "TypeName" } -} - -/** - * A name referring to a value, that is, a constant, variable or function. - * - * Examples: - * - * ```go - * c - * f - * x - * ``` - */ -class ValueName extends Name { - override ValueEntity target; - - /** Gets the constant, variable or function this name refers to. */ - override ValueEntity getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "ValueName" } -} - -/** - * A name referring to a constant. - * - * Examples: - * - * ```go - * c - * ``` - */ -class ConstantName extends ValueName { - override Constant target; - - /** Gets the constant this name refers to. */ - override Constant getTarget() { result = target } - - override predicate isPlatformIndependentConstant() { - target = Builtin::bool(_) - or - target = Builtin::iota() - or - target = Builtin::nil() - or - exists(DeclaredConstant c | c = target | - not c.getSpec().getFile().hasBuildConstraints() and - c.getInit().isPlatformIndependentConstant() - ) - } - - override string getAPrimaryQlClass() { result = "ConstantName" } -} - -/** - * A name referring to a variable. - * - * Examples: - * - * ```go - * x - * ``` - */ -class VariableName extends ValueName { - override Variable target; - - /** Gets the variable this name refers to. */ - override Variable getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "VariableName" } -} - -/** - * A name referring to a function. - * - * Examples: - * - * ```go - * f - * ``` - */ -class FunctionName extends ValueName { - override Function target; - - /** Gets the function this name refers to. */ - override Function getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "FunctionName" } -} - -/** - * A name referring to a statement label. - * - * Examples: - * - * ```go - * Outerloop - * ``` - */ -class LabelName extends Name { - override Label target; - - /** Gets the label this name refers to. */ - override Label getTarget() { result = target } - - override string getAPrimaryQlClass() { result = "LabelName" } -} - -/** - * Holds if `e` is a type expression, as determined by a bottom-up syntactic - * analysis starting with `TypeName`s. - * - * On a snapshot with full type information, this predicate covers all type - * expressions. However, if type information is missing then not all type names - * may be identified as such, so not all type expressions can be determined by - * a bottom-up analysis. In such cases, `isTypeExprTopDown` below is useful. - */ -private predicate isTypeExprBottomUp(Expr e) { - e instanceof TypeName - or - e instanceof @arraytypeexpr - or - e instanceof @structtypeexpr - or - e instanceof @functypeexpr - or - e instanceof @interfacetypeexpr - or - e instanceof @maptypeexpr - or - e instanceof @chantypeexpr - or - e instanceof @typesetliteralexpr - or - e instanceof @generictypeinstantiationexpr - or - e instanceof @indexexpr and isTypeExprBottomUp(e.getChildExpr(0)) - or - isTypeExprBottomUp(e.(ParenExpr).getExpr()) - or - isTypeExprBottomUp(e.(StarExpr).getBase()) - or - isTypeExprBottomUp(e.(Ellipsis).getOperand()) -} - -/** - * Holds if `e` must be a type expression because it either occurs in a syntactic - * position where a type is expected, or it is part of a larger type expression. - * - * This predicate is only needed on snapshots for which type information is - * incomplete. It is an underapproximation; in cases where it is syntactically ambiguous - * whether an expression refers to a type or a value, we conservatively assume that - * it may be the latter and so this predicate does not consider the expression to be - * a type expression. - */ -private predicate isTypeExprTopDown(Expr e) { - e = any(CompositeLit cl).getTypeExpr() - or - e = any(TypeAssertExpr ta).getTypeExpr() - or - e = any(ArrayTypeExpr ae).getElement() - or - e = any(FieldDecl f).getTypeExpr() - or - e = any(ParameterDecl pd).getTypeExpr() - or - e = any(TypeParamDecl tpd).getTypeConstraintExpr() - or - e = any(TypeParamDecl tpd).getNameExpr(_) - or - e = any(ReceiverDecl rd).getTypeExpr() - or - e = any(ResultVariableDecl rvd).getTypeExpr() - or - e = any(MethodSpec md).getTypeExpr() - or - e = any(MapTypeExpr mt).getKeyTypeExpr() - or - e = any(MapTypeExpr mt).getValueTypeExpr() - or - e = any(ChanTypeExpr ct).getValueTypeExpr() - or - e = any(ValueSpec s).getTypeExpr() - or - e = any(TypeSpec s).getTypeExpr() - or - e = any(GenericTypeInstantiationExpr gtie).getBase() - or - e = any(GenericTypeInstantiationExpr gtie).getTypeArgument(_) - or - e = any(TypeSwitchStmt s).getACase().getExpr(_) and - // special case: `nil` is allowed in a type case but isn't a type - not e = Builtin::nil().getAReference() - or - e = any(SelectorExpr sel | isTypeExprTopDown(sel)).getBase() - or - e = any(ParenExpr pe | isTypeExprTopDown(pe)).getExpr() - or - e = any(StarExpr se | isTypeExprTopDown(se)).getBase() - or - e = any(Ellipsis ell | isTypeExprTopDown(ell)).getOperand() -} - -/** - * An expression referring to a type. - * - * Examples: - * - * ```go - * int - * func - * ``` - */ -class TypeExpr extends Expr { - TypeExpr() { - isTypeExprBottomUp(this) or - isTypeExprTopDown(this) - } -} - -/** - * An expression referring to a memory location. - * - * Examples: - * - * ```go - * a[i] - * *p - * ``` - */ -class ReferenceExpr extends Expr { - ReferenceExpr() { - (this instanceof Ident or this instanceof SelectorExpr) and - not (this instanceof PackageName or this instanceof TypeName or this instanceof LabelName) and - not this instanceof TypeExpr and - not this = any(ImportSpec is).getNameExpr() and - not this = any(File f).getPackageNameExpr() and - not this = any(LabeledStmt ls).getLabelExpr() and - not this = any(BranchStmt bs).getLabelExpr() and - not this = any(FieldDecl f).getNameExpr(_) and - not this = any(ParameterDecl pd).getNameExpr(_) and - not this = any(ReceiverDecl rd).getNameExpr() and - not this = any(ResultVariableDecl rvd).getNameExpr(_) and - not this = any(MethodSpec md).getNameExpr() and - not this = any(StructLit sl).getKey(_) - or - this.(ParenExpr).getExpr() instanceof ReferenceExpr - or - this.(StarExpr).getBase() instanceof ReferenceExpr - or - this instanceof DerefExpr - or - this instanceof IndexExpr - } - - /** Holds if this reference expression occurs in a position where it is being assigned to. */ - predicate isLvalue() { - this = any(Assignment assgn).getLhs(_) - or - this = any(IncDecStmt ids).getOperand() - or - exists(RangeStmt rs | - this = rs.getKey() or - this = rs.getValue() - ) - or - exists(ValueSpec spec, int i | this = spec.getNameExpr(i)) - or - exists(FuncDecl fd | this = fd.getNameExpr()) - } - - /** Holds if this reference expression occurs in a position where it is evaluated to a value. */ - predicate isRvalue() { - not this.isLvalue() - or - this = any(CompoundAssignStmt cmp).getLhs(_) - or - this = any(IncDecStmt ids).getOperand() - } -} - -/** - * An expression that refers to a value (as opposed to a package, a type or a statement label). - * - * Examples: - * - * ```go - * x + y - * f(x) - * ``` - */ -class ValueExpr extends Expr { - ValueExpr() { - this.(ReferenceExpr).isRvalue() or - this instanceof BasicLit or - this instanceof FuncLit or - this instanceof CompositeLit or - this.(ParenExpr).getExpr() instanceof ValueExpr or - this instanceof SliceExpr or - this instanceof TypeAssertExpr or - this instanceof CallOrConversionExpr or - this.(StarExpr).getBase() instanceof ValueExpr or - this instanceof OperatorExpr - } -} diff --git a/ql/lib/semmle/go/Files.qll b/ql/lib/semmle/go/Files.qll deleted file mode 100644 index 825b40f8b..000000000 --- a/ql/lib/semmle/go/Files.qll +++ /dev/null @@ -1,288 +0,0 @@ -/** Provides classes for working with files and folders. */ - -import go - -/** A file or folder. */ -abstract class Container extends @container { - /** - * Gets the absolute, canonical path of this container, using forward slashes - * as path separator. - * - * The path starts with a _root prefix_ followed by zero or more _path - * segments_ separated by forward slashes. - * - * The root prefix is of one of the following forms: - * - * 1. A single forward slash `/` (Unix-style) - * 2. An upper-case drive letter followed by a colon and a forward slash, - * such as `C:/` (Windows-style) - * 3. Two forward slashes, a computer name, and then another forward slash, - * such as `//FileServer/` (UNC-style) - * - * Path segments are never empty (that is, absolute paths never contain two - * contiguous slashes, except as part of a UNC-style root prefix). Also, path - * segments never contain forward slashes, and no path segment is of the - * form `.` (one dot) or `..` (two dots). - * - * Note that an absolute path never ends with a forward slash, except if it is - * a bare root prefix, that is, the path has no path segments. A container - * whose absolute path has no segments is always a `Folder`, not a `File`. - */ - abstract string getAbsolutePath(); - - /** - * Gets a URL representing the location of this container. - * - * For more information see https://lgtm.com/help/ql/locations#providing-urls. - */ - abstract string getURL(); - - /** - * Gets the relative path of this file or folder from the root folder of the - * analyzed source location. The relative path of the root folder itself is - * the empty string. - * - * This has no result if the container is outside the source root, that is, - * if the root folder is not a reflexive, transitive parent of this container. - */ - string getRelativePath() { - exists(string absPath, string pref | - absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) - | - absPath = pref and result = "" - or - absPath = pref.regexpReplaceAll("/$", "") + "/" + result and - not result.matches("/%") - ) - } - - /** - * Gets the base name of this container including extension, that is, the last - * segment of its absolute path, or the empty string if it has no segments. - * - * Here are some examples of absolute paths and the corresponding base names - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - * - *
Absolute pathBase name
"/tmp/tst.go""tst.go"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
- */ - string getBaseName() { - result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) - } - - /** - * Gets the extension of this container, that is, the suffix of its base name - * after the last dot character, if any. - * - * In particular, - * - * - if the name does not include a dot, there is no extension, so this - * predicate has no result; - * - if the name ends in a dot, the extension is the empty string; - * - if the name contains multiple dots, the extension follows the last dot. - * - * Here are some examples of absolute paths and the corresponding extensions - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathExtension
"/tmp/tst.go""go"
"/tmp/.classpath""classpath"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
- */ - string getExtension() { - result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) - } - - /** - * Gets the stem of this container, that is, the prefix of its base name up to - * (but not including) the last dot character if there is one, or the entire - * base name if there is not. - * - * Here are some examples of absolute paths and the corresponding stems - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathStem
"/tmp/tst.go""tst"
"/tmp/.classpath"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
- */ - string getStem() { - result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) - } - - /** Gets the parent container of this file or folder, if any. */ - Container getParentContainer() { containerparent(result, this) } - - /** Gets a file or sub-folder in this container. */ - Container getAChildContainer() { this = result.getParentContainer() } - - /** Gets a file in this container. */ - File getAFile() { result = this.getAChildContainer() } - - /** Gets the file in this container that has the given `baseName`, if any. */ - File getFile(string baseName) { - result = this.getAFile() and - result.getBaseName() = baseName - } - - /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = this.getAChildContainer() } - - /** Gets the sub-folder in this container that has the given `baseName`, if any. */ - Folder getFolder(string baseName) { - result = this.getAFolder() and - result.getBaseName() = baseName - } - - /** - * Gets a textual representation of the path of this container. - * - * This is the absolute path of the container. - */ - string toString() { result = this.getAbsolutePath() } -} - -/** A folder. */ -class Folder extends Container, @folder { - override string getAbsolutePath() { folders(this, result) } - - /** Gets the file or subfolder in this folder that has the given `name`, if any. */ - Container getChildContainer(string name) { - result = this.getAChildContainer() and - result.getBaseName() = name - } - - /** Gets the file in this folder that has the given `stem` and `extension`, if any. */ - File getFile(string stem, string extension) { - result = this.getAChildContainer() and - result.getStem() = stem and - result.getExtension() = extension - } - - /** Gets a subfolder contained in this folder. */ - Folder getASubFolder() { result = this.getAChildContainer() } - - /** Gets the URL of this folder. */ - override string getURL() { result = "folder://" + this.getAbsolutePath() } -} - -/** Any file, including files that have not been extracted but are referred to as locations for errors. */ -class ExtractedOrExternalFile extends Container, @file, Documentable, ExprParent, GoModExprParent, - DeclParent, ScopeNode { - override Location getLocation() { has_location(this, result) } - - override string getAbsolutePath() { files(this, result) } - - /** Gets the number of lines in this file. */ - int getNumberOfLines() { numlines(this, result, _, _) } - - /** Gets the number of lines containing code in this file. */ - int getNumberOfLinesOfCode() { numlines(this, _, result, _) } - - /** Gets the number of lines containing comments in this file. */ - int getNumberOfLinesOfComments() { numlines(this, _, _, result) } - - /** Gets the package name as specified in the package clause of this file. */ - Ident getPackageNameExpr() { result = this.getChildExpr(0) } - - /** Gets the name of the package to which this file belongs. */ - string getPackageName() { result = this.getPackageNameExpr().getName() } - - /** Holds if this file contains at least one build constraint. */ - pragma[noinline] - predicate hasBuildConstraints() { exists(BuildConstraintComment bc | this = bc.getFile()) } - - /** - * Holds if this file contains build constraints that ensure that it - * is only built on architectures of bit size `bitSize`, which can be - * 32 or 64. - */ - predicate constrainsIntBitSize(int bitSize) { - this.explicitlyConstrainsIntBitSize(bitSize) or - this.implicitlyConstrainsIntBitSize(bitSize) - } - - /** - * Holds if this file contains explicit build constraints that ensure - * that it is only built on an architecture of bit size `bitSize`, - * which can be 32 or 64. - */ - predicate explicitlyConstrainsIntBitSize(int bitSize) { - exists(BuildConstraintComment bcc | this = bcc.getFile() | - forex(string disjunct | disjunct = bcc.getADisjunct() | - disjunct.splitAt(",").(Architecture).getBitSize() = bitSize - or - disjunct.splitAt("/").(Architecture).getBitSize() = bitSize - ) - ) - } - - /** - * Holds if this file has a name which acts as an implicit build - * constraint that ensures that it is only built on an - * architecture of bit size `bitSize`, which can be 32 or 64. - */ - predicate implicitlyConstrainsIntBitSize(int bitSize) { - exists(Architecture arch | arch.getBitSize() = bitSize | - this.getStem().regexpMatch("(?i).*_\\Q" + arch + "\\E(_test)?") - ) - } - - override string toString() { result = Container.super.toString() } - - /** Gets the URL of this file. */ - override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } - - /** Gets the `i`th child comment group. */ - CommentGroup getCommentGroup(int i) { comment_groups(result, this, i) } - - /** Gets a child comment group. */ - CommentGroup getACommentGroup() { result = this.getCommentGroup(_) } - - /** Gets the number of child comment groups of this file. */ - int getNumCommentGroups() { result = count(this.getACommentGroup()) } - - override string getAPrimaryQlClass() { result = "ExtractedOrExternalFile" } -} - -/** A file that has been extracted. */ -class File extends ExtractedOrExternalFile { - File() { - // getAChild is specifically for the Go AST and so does not apply to non-go files - // we care about all non-go extracted files, as only go files can have `@file` entries due to requiring a file entry for diagnostic errors - not this.getExtension() = "go" - or - exists(this.getAChild()) - } - - override string getAPrimaryQlClass() { result = "File" } -} - -/** A Go file. */ -class GoFile extends File { - GoFile() { this.getExtension() = "go" } - - override string getAPrimaryQlClass() { result = "GoFile" } -} - -/** An HTML file. */ -class HtmlFile extends File { - HtmlFile() { this.getExtension().regexpMatch("x?html?") } - - override string getAPrimaryQlClass() { result = "HtmlFile" } -} diff --git a/ql/lib/semmle/go/GoMod.qll b/ql/lib/semmle/go/GoMod.qll deleted file mode 100644 index 119339c18..000000000 --- a/ql/lib/semmle/go/GoMod.qll +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Provides classes for working with go.mod files. - */ - -import go - -/** A go.mod file. */ -class GoModFile extends File { - GoModFile() { this.getBaseName() = "go.mod" } - - /** - * Gets the module declaration of this file, that is, the line declaring the path of this module. - */ - GoModModuleLine getModuleDeclaration() { result.getFile() = this } - - override string getAPrimaryQlClass() { result = "GoModFile" } -} - -/** - * An expression in a go.mod file, which is used to declare dependencies. - */ -class GoModExpr extends @modexpr, GoModExprParent { - /** - * Gets the kind of this expression, which is an integer value representing the expression's - * node type. - * - * Note that the mapping from node types to integer kinds is considered an implementation detail - * and subject to change without notice. - */ - int getKind() { modexprs(this, result, _, _) } - - /** - * Get the comment group associated with this expression. - */ - DocComment getComments() { result.getDocumentedElement() = this } - - override GoModFile getFile() { result = GoModExprParent.super.getFile() } - - /** Gets path of the module of this go.mod expression. */ - string getModulePath() { result = this.getFile().getModuleDeclaration().getPath() } - - override string toString() { result = "go.mod expression" } - - override string getAPrimaryQlClass() { result = "GoModExpr" } -} - -/** - * A top-level block of comments separate from any rule. - */ -class GoModCommentBlock extends @modcommentblock, GoModExpr { - override string getAPrimaryQlClass() { result = "GoModCommentBlock" } -} - -/** - * A single line of tokens. - */ -class GoModLine extends @modline, GoModExpr { - /** - * Gets the `i`th token on this line, 0-based. - * - * Generally, one should use `getToken`, as that accounts for lines inside of line blocks. - */ - string getRawToken(int i) { modtokens(result, this, i) } - - /** - * Gets the `i`th token of `line`, including the token in the line block declaration, if it there is - * one, 0-based. - * - * This compensates for the fact that lines in line blocks have their 0th token in the line block - * declaration, and makes dealing with lines more uniform. - * - * For example, `.getToken(1)` will result in the dependency path (`github.com/github/codeql-go`) - * for both lines for normal require lines like `require "github.com/github/codeql-go" v1.2.3` and - * in a line block like - * - * ``` - * require ( - * "github.com/github/codeql-go" v1.2.3 - * ... - * ) - * ``` - * - * As a special case, when `i` is `0` and the line is in a line block, the result will be the - * token from the line block. - */ - string getToken(int i) { - i = 0 and result = this.getParent().(GoModLineBlock).getRawToken(0) - or - if this.getParent() instanceof GoModLineBlock - then result = this.getRawToken(i - 1) - else result = this.getRawToken(i) - } - - override string toString() { result = "go.mod line" } - - override string getAPrimaryQlClass() { result = "GoModLine" } -} - -/** - * A factored block of lines, for example: - * ``` - * require ( - * "github.com/github/codeql-go" v1.2.3 - * "golang.org/x/tools" v3.2.1 - * ) - * ``` - */ -class GoModLineBlock extends @modlineblock, GoModExpr { - /** - * Gets the `i`th token of this line block, 0-based. - * - * Usually one should not have to use this, as `GoModLine.getToken(0)` will get the token from its - * parent line block, if any. - */ - string getRawToken(int i) { modtokens(result, this, i) } - - override string toString() { result = "go.mod line block" } - - override string getAPrimaryQlClass() { result = "GoModLineBlock" } -} - -/** - * A line that contains the module's package path, for example `module github.com/github/codeql-go`. - */ -class GoModModuleLine extends GoModLine { - GoModModuleLine() { this.getToken(0) = "module" } - - /** - * Get the path of the module being declared. - */ - string getPath() { result = this.getToken(1) } - - override string toString() { result = "go.mod module line" } - - override string getAPrimaryQlClass() { result = "GoModModuleLine" } -} - -/** - * A line that declares the Go version to be used, for example `go 1.14`. - */ -class GoModGoLine extends GoModLine { - GoModGoLine() { this.getToken(0) = "go" } - - /** Gets the Go version declared. */ - string getVersion() { result = this.getToken(1) } - - override string toString() { result = "go.mod go line" } - - override string getAPrimaryQlClass() { result = "GoModGoLine" } -} - -/** - * A line that declares a requirement, for example `require "github.com/github/codeql-go" v1.2.3`. - */ -class GoModRequireLine extends GoModLine { - GoModRequireLine() { this.getToken(0) = "require" } - - /** Gets the path of the dependency. */ - string getPath() { result = this.getToken(1) } - - /** Gets the version of the dependency. */ - string getVersion() { result = this.getToken(2) } - - override string toString() { result = "go.mod require line" } - - override string getAPrimaryQlClass() { result = "GoModRequireLine" } -} - -/** - * A line that declares a dependency version to exclude, for example - * `exclude "github.com/github/codeql-go" v1.2.3`. - */ -class GoModExcludeLine extends GoModLine { - GoModExcludeLine() { this.getToken(0) = "exclude" } - - /** Gets the path of the dependency to exclude a version of. */ - string getPath() { result = this.getToken(1) } - - /** Gets the excluded version. */ - string getVersion() { result = this.getToken(2) } - - override string toString() { result = "go.mod exclude line" } - - override string getAPrimaryQlClass() { result = "GoModExcludeLine" } -} - -/** - * A line that specifies a dependency to use instead of another one, for example - * `replace "golang.org/x/tools" => "github.com/golang/tools" v1.2.3`. - */ -class GoModReplaceLine extends GoModLine { - GoModReplaceLine() { this.getToken(0) = "replace" } - - /** Gets the path of the dependency to be replaced. */ - string getOriginalPath() { result = this.getToken(1) } - - /** Gets the path of the dependency to be replaced, if any. */ - string getOriginalVersion() { result = this.getToken(2) and not result = "=>" } - - /** Gets the path of the replacement dependency. */ - string getReplacementPath() { - if exists(this.getOriginalVersion()) - then result = this.getToken(4) - else result = this.getToken(3) - } - - /** Gets the version of the replacement dependency. */ - string getReplacementVersion() { - if exists(this.getOriginalVersion()) - then result = this.getToken(5) - else result = this.getToken(4) - } - - override string toString() { result = "go.mod replace line" } - - override string getAPrimaryQlClass() { result = "GoModReplaceLine" } -} - -/** A left parenthesis for a line block. */ -class GoModLParen extends @modlparen, GoModExpr { - override string toString() { result = "go.mod (" } - - override string getAPrimaryQlClass() { result = "GoModLParen" } -} - -/** A right parenthesis for a line block. */ -class GoModRParen extends @modrparen, GoModExpr { - override string toString() { result = "go.mod )" } - - override string getAPrimaryQlClass() { result = "GoModRParen" } -} diff --git a/ql/lib/semmle/go/HTML.qll b/ql/lib/semmle/go/HTML.qll deleted file mode 100644 index f4fb773ca..000000000 --- a/ql/lib/semmle/go/HTML.qll +++ /dev/null @@ -1,207 +0,0 @@ -/** Provides classes for working with HTML documents. */ - -import go - -module HTML { - /** - * An HTML element. - * - * Example: - * - * ``` - * Semmle - * ``` - */ - class Element extends Locatable, @xmlelement { - Element() { exists(HtmlFile f | xmlElements(this, _, _, _, f)) } - - override Location getLocation() { xmllocations(this, result) } - - /** - * Gets the name of this HTML element. - * - * For example, the name of `
` is `br`. - */ - string getName() { xmlElements(this, result, _, _, _) } - - /** - * Gets the parent element of this element, if any. - */ - Element getParent() { xmlElements(this, _, result, _, _) } - - /** - * Holds if this is a toplevel element, that is, if it does not have a parent element. - */ - predicate isTopLevel() { not exists(getParent()) } - - /** - * Gets the root HTML document element in which this element is contained. - */ - DocumentElement getDocument() { result = getRoot() } - - /** - * Gets the root element in which this element is contained. - */ - Element getRoot() { if isTopLevel() then result = this else result = getParent().getRoot() } - - /** - * Gets the `i`th child element (0-based) of this element. - */ - Element getChild(int i) { xmlElements(result, _, this, i, _) } - - /** - * Gets a child element of this element. - */ - Element getChild() { result = getChild(_) } - - /** - * Gets the `i`th attribute (0-based) of this element. - */ - Attribute getAttribute(int i) { xmlAttrs(result, this, _, _, i, _) } - - /** - * Gets an attribute of this element. - */ - Attribute getAnAttribute() { result = getAttribute(_) } - - /** - * Gets an attribute of this element that has the given name. - */ - Attribute getAttributeByName(string name) { - result = getAnAttribute() and - result.getName() = name - } - - /** - * Gets the text node associated with this element. - */ - TextNode getTextNode() { result.getParent() = this } - - override string toString() { result = "<" + getName() + ">..." } - } - - /** - * An attribute of an HTML element. - * - * Examples: - * - * ``` - * - * target=_blank - * >Semmle - * ``` - */ - class Attribute extends Locatable, @xmlattribute { - Attribute() { xmlAttrs(this, _, _, _, _, any(HtmlFile f)) } - - override Location getLocation() { xmllocations(this, result) } - - /** - * Gets the element to which this attribute belongs. - */ - Element getElement() { xmlAttrs(this, result, _, _, _, _) } - - /** - * Gets the root element in which the element to which this attribute - * belongs is contained. - */ - Element getRoot() { result = getElement().getRoot() } - - /** - * Gets the name of this attribute. - */ - string getName() { xmlAttrs(this, _, result, _, _, _) } - - /** - * Gets the value of this attribute. - * - * For attributes without an explicitly specified value, the - * result is the empty string. - */ - string getValue() { xmlAttrs(this, _, _, result, _, _) } - - override string toString() { result = getName() + "=" + getValue() } - } - - /** - * An HTML `` element. - * - * Example: - * - * ``` - * - * - * This is a test. - * - * - * ``` - */ - class DocumentElement extends Element { - DocumentElement() { getName() = "html" } - } - - /** - * An HTML text node. - * - * Example: - * - * ``` - *
- * This text is represented as a text node. - *
- * ``` - */ - class TextNode extends Locatable, @xmlcharacters { - TextNode() { exists(HtmlFile f | xmlChars(this, _, _, _, _, f)) } - - override string toString() { result = getText() } - - /** - * Gets the content of this text node. - * - * Note that entity expansion has been performed already. - */ - string getText() { xmlChars(this, result, _, _, _, _) } - - /** - * Gets the parent this text. - */ - Element getParent() { xmlChars(this, _, result, _, _, _) } - - /** - * Gets the child index number of this text node. - */ - int getIndex() { xmlChars(this, _, _, result, _, _) } - - /** - * Holds if this text node is inside a `CDATA` tag. - */ - predicate isCData() { xmlChars(this, _, _, _, 1, _) } - - override Location getLocation() { xmllocations(this, result) } - } - - /** - * An HTML comment. - * - * Example: - * - * ``` - * - * ``` - */ - class CommentNode extends Locatable, @xmlcomment { - CommentNode() { exists(HtmlFile f | xmlComments(this, _, _, f)) } - - /** Gets the element in which this comment occurs. */ - Element getParent() { xmlComments(this, _, result, _) } - - /** Gets the text of this comment, not including delimiters. */ - string getText() { result = toString().regexpCapture("(?s)", 1) } - - override string toString() { xmlComments(this, result, _, _) } - - override Location getLocation() { xmllocations(this, result) } - } -} diff --git a/ql/lib/semmle/go/Locations.qll b/ql/lib/semmle/go/Locations.qll deleted file mode 100644 index 4fb69be21..000000000 --- a/ql/lib/semmle/go/Locations.qll +++ /dev/null @@ -1,81 +0,0 @@ -/** Provides classes for working with locations and program elements that have locations. */ - -import go - -/** - * A location as given by a file, a start line, a start column, - * an end line, and an end column. - * - * For more information about locations see [LGTM locations](https://lgtm.com/help/ql/locations). - */ -class Location extends @location { - /** Gets the file for this location. */ - File getFile() { locations_default(this, result, _, _, _, _) } - - /** Gets the 1-based line number (inclusive) where this location starts. */ - int getStartLine() { locations_default(this, _, result, _, _, _) } - - /** Gets the 1-based column number (inclusive) where this location starts. */ - int getStartColumn() { locations_default(this, _, _, result, _, _) } - - /** Gets the 1-based line number (inclusive) where this location ends. */ - int getEndLine() { locations_default(this, _, _, _, result, _) } - - /** Gets the 1-based column number (inclusive) where this location ends. */ - int getEndColumn() { locations_default(this, _, _, _, _, result) } - - /** Gets the number of lines covered by this location. */ - int getNumLines() { result = getEndLine() - getStartLine() + 1 } - - /** Gets a textual representation of this element. */ - string toString() { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn - ) - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [LGTM locations](https://lgtm.com/help/ql/locations). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - exists(File f | - locations_default(this, f, startline, startcolumn, endline, endcolumn) and - filepath = f.getAbsolutePath() - ) - } -} - -/** A program element with a location. */ -class Locatable extends @locatable { - /** Gets the file this program element comes from. */ - File getFile() { result = getLocation().getFile() } - - /** Gets this element's location. */ - Location getLocation() { has_location(this, result) } - - /** Gets the number of lines covered by this element. */ - int getNumLines() { result = getLocation().getNumLines() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [LGTM locations](https://lgtm.com/help/ql/locations). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets a textual representation of this element. */ - string toString() { result = "locatable element" } -} diff --git a/ql/lib/semmle/go/Packages.qll b/ql/lib/semmle/go/Packages.qll deleted file mode 100644 index bc51911da..000000000 --- a/ql/lib/semmle/go/Packages.qll +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Provides classes for working with packages. - */ - -import go - -/** - * A package. - */ -class Package extends @package { - /** Gets the name of this package. */ - string getName() { packages(this, result, _, _) } - - /** Gets the path of this package. */ - string getPath() { - exists(string fullPath | packages(this, _, fullPath, _) | - result = fullPath.regexpReplaceAll("^.*/vendor/", "") - ) - } - - /** Gets the scope of this package. */ - PackageScope getScope() { packages(this, _, _, result) } - - /** Gets a textual representation of this element. */ - string toString() { result = "package " + getPath() } -} - -/** - * Gets an import path that identifies a package in module `mod` with the given path, - * possibly modulo [semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning). - * - * For example, `package("github.com/go-pg/pg", "types")` gets an import path that can - * refer to `"github.com/go-pg/pg/types"`, but also to `"github.com/go-pg/pg/v10/types"`. - */ -bindingset[mod, path] -string package(string mod, string path) { - // "\Q" and "\E" start and end a quoted section of a regular expression. Anything like "." or "*" that - // "*" that comes between them is not interpreted as it would normally be in a regular expression. - result.regexpMatch("\\Q" + mod + "\\E([/.]v[^/]+)?($|/)\\Q" + path + "\\E") and - result = any(Package p).getPath() -} diff --git a/ql/lib/semmle/go/PrintAst.ql b/ql/lib/semmle/go/PrintAst.ql deleted file mode 100644 index 23b6b123b..000000000 --- a/ql/lib/semmle/go/PrintAst.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Print AST - * @description Outputs a representation of the Abstract Syntax Tree. - * @id go/print-ast - * @kind graph - */ - -import go -import PrintAst - -/** - * Hook to customize the functions printed by this query. - */ -class Cfg extends PrintAstConfiguration { - override predicate shouldPrintFunction(FuncDecl func) { any() } - - override predicate shouldPrintFile(File file) { any() } - - override predicate shouldPrintComments(File file) { any() } -} diff --git a/ql/lib/semmle/go/PrintAst.qll b/ql/lib/semmle/go/PrintAst.qll deleted file mode 100644 index cf28be44f..000000000 --- a/ql/lib/semmle/go/PrintAst.qll +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Provides queries to pretty-print a Go AST as a graph. - */ - -import go - -/** - * Hook to customize the files and functions printed by this module. - * - * For an AstNode to be printed, it always requires `shouldPrintFile(f)` to hold - * for its containing file `f`, and additionally requires `shouldPrintFunction(fun)` - * to hold if it is, or is a child of, function `fun`. - */ -class PrintAstConfiguration extends string { - /** - * Restrict to a single string, making this a singleton type. - */ - PrintAstConfiguration() { this = "PrintAstConfiguration" } - - /** - * Holds if the AST for `func` should be printed. By default, holds for all - * functions. - */ - predicate shouldPrintFunction(FuncDecl func) { any() } - - /** - * Holds if the AST for `file` should be printed. By default, holds for all - * files. - */ - predicate shouldPrintFile(File file) { any() } - - /** - * Holds if the AST for `file` should include comments. By default, holds for all - * files. - */ - predicate shouldPrintComments(File file) { any() } -} - -private predicate shouldPrintFunction(FuncDef func) { - exists(PrintAstConfiguration config | config.shouldPrintFunction(func)) -} - -private predicate shouldPrintFile(File file) { - exists(PrintAstConfiguration config | config.shouldPrintFile(file)) -} - -private predicate shouldPrintComments(File file) { - exists(PrintAstConfiguration config | config.shouldPrintComments(file)) -} - -private FuncDecl getEnclosingFunctionDecl(AstNode n) { result = n.getParent*() } - -/** - * An AST node that should be printed. - */ -private newtype TPrintAstNode = - TAstNode(AstNode ast) { - shouldPrintFile(ast.getFile()) and - // Do print ast nodes without an enclosing function, e.g. file headers, that are not otherwise excluded - forall(FuncDecl f | f = getEnclosingFunctionDecl(ast) | shouldPrintFunction(f)) and - ( - shouldPrintComments(ast.getFile()) - or - not ast instanceof Comment and not ast instanceof CommentGroup - ) - } - -/** - * A node in the output tree. - */ -class PrintAstNode extends TPrintAstNode { - /** - * Gets a textual representation of this node. - */ - abstract string toString(); - - /** - * Gets the child node at index `childIndex`. Child indices must be unique, - * but need not be contiguous. - */ - abstract PrintAstNode getChild(int childIndex); - - /** - * Holds if this node should be printed in the output. By default, all nodes - * within a function are printed, but the query can override - * `PrintAstConfiguration.shouldPrintFunction` to filter the output. - */ - predicate shouldPrint() { exists(getLocation()) } - - /** - * Gets a child of this node. - */ - PrintAstNode getAChild() { result = getChild(_) } - - /** - * Gets the location of this node in the source code. - */ - abstract Location getLocation(); - - /** - * Gets the value of the property of this node, where the name of the property - * is `key`. - */ - string getProperty(string key) { - key = "semmle.label" and - result = toString() - } - - /** - * Gets the label for the edge from this node to the specified child. By - * default, this is just the index of the child, but subclasses can override - * this. - */ - string getChildEdgeLabel(int childIndex) { - exists(getChild(childIndex)) and - result = childIndex.toString() - } - - /** - * Gets the `FuncDef` that contains this node. - */ - abstract FuncDef getEnclosingFunction(); -} - -/** - * Gets a pretty-printed representation of the QL class(es) for entity `el`. - */ -private string qlClass(AstNode el) { - // This version shows all non-overridden QL classes: - // result = "[" + concat(el.getAQlClass(), ", ") + "] " - // Normally we prefer to show just the canonical class: - result = "[" + concat(el.getAPrimaryQlClass(), ", ") + "] " -} - -/** - * A graph node representing a real AST node. - */ -class BaseAstNode extends PrintAstNode, TAstNode { - AstNode ast; - - BaseAstNode() { this = TAstNode(ast) } - - override BaseAstNode getChild(int childIndex) { - // Note a node can have several results for getChild(n) because some - // nodes have multiple different types of child (e.g. a File has a - // child expression, the package name, and child declarations whose - // indices may clash), so we renumber them: - result = TAstNode(ast.getUniquelyNumberedChild(childIndex)) - } - - override string toString() { result = qlClass(ast) + ast } - - final override Location getLocation() { result = ast.getLocation() } - - final override FuncDef getEnclosingFunction() { - result = ast or result = ast.getEnclosingFunction() - } -} - -/** - * A node representing an `Expr`. - */ -class ExprNode extends BaseAstNode { - override Expr ast; - - override string getProperty(string key) { - result = super.getProperty(key) - or - key = "Value" and - result = qlClass(ast) + ast.getExactValue() - or - key = "Type" and - not ast.getType() instanceof InvalidType and - result = ast.getType().pp() - } -} - -/** - * A node representing a `File` - */ -class FileNode extends BaseAstNode { - override File ast; - - private string getRelativePath() { result = ast.getRelativePath() } - - private int getSortOrder() { - rank[result](FileNode fn | any() | fn order by fn.getRelativePath()) = this - } - - override string getProperty(string key) { - result = super.getProperty(key) - or - key = "semmle.order" and - result = getSortOrder().toString() - } - - /** - * Gets a child of this node, renumbering `packageNode`, our parent's - * `oldPackageIndex`th child, as the first child and moving others accordingly. - */ - private BaseAstNode getChildPackageFirst( - int childIndex, BaseAstNode packageNode, int oldPackageIndex - ) { - super.getChild(oldPackageIndex) = packageNode and - ( - childIndex = 0 and result = packageNode - or - result = - rank[childIndex](BaseAstNode node, int i | - node = super.getChild(i) and i != oldPackageIndex - | - node order by i - ) - ) - } - - /** - * Gets a child of this node, moving the package-name expression to the front - * of the list if one exists. - */ - override BaseAstNode getChild(int childIndex) { - if exists(ast.getPackageNameExpr()) - then result = getChildPackageFirst(childIndex, TAstNode(ast.getPackageNameExpr()), _) - else result = super.getChild(childIndex) - } - - /** - * Gets the label for the edge from this node to the specified child. The package name - * expression is named 'package'; others are numbered as per our parent's implementation - * of this method. - */ - override string getChildEdgeLabel(int childIndex) { - if getChild(childIndex) = TAstNode(ast.getPackageNameExpr()) - then result = "package" - else result = super.getChildEdgeLabel(childIndex) - } - - /** - * Gets the string representation of this File. Note explicitly using a relative path - * like this rather than absolute as per default for the File class is a workaround for - * a bug with codeql run test, which should replace absolute paths but currently does not. - */ - override string toString() { result = qlClass(ast) + ast.getRelativePath() } -} - -/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ -query predicate nodes(PrintAstNode node, string key, string value) { - node.shouldPrint() and - value = node.getProperty(key) -} - -/** - * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the - * given `value`. - */ -query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { - exists(int childIndex | - source.shouldPrint() and - target.shouldPrint() and - target = source.getChild(childIndex) - | - key = "semmle.label" and value = source.getChildEdgeLabel(childIndex) - or - key = "semmle.order" and value = childIndex.toString() - ) -} - -/** Holds if property `key` of the graph has the given `value`. */ -query predicate graphProperties(string key, string value) { - key = "semmle.graphKind" and value = "tree" -} diff --git a/ql/lib/semmle/go/Scopes.qll b/ql/lib/semmle/go/Scopes.qll deleted file mode 100644 index 02ead4b94..000000000 --- a/ql/lib/semmle/go/Scopes.qll +++ /dev/null @@ -1,818 +0,0 @@ -/** - * Provides classes for working with scopes and declared objects. - */ - -import go - -/** - * A scope. - */ -class Scope extends @scope { - /** Gets the enclosing scope of this scope, if any. */ - Scope getOuterScope() { scopenesting(this, result) } - - /** Gets a scope nested inside this scope. */ - Scope getAnInnerScope() { this = result.getOuterScope() } - - /** Looks up the entity with the given name in this scope. */ - Entity getEntity(string name) { - result.getName() = name and - result.getScope() = this - } - - /** Gets a textual representation of this scope. */ - string toString() { result = "scope" } -} - -/** Provides helper predicates for working with scopes. */ -module Scope { - /** Gets the universe scope. */ - UniverseScope universe() { any() } -} - -/** - * The universe scope. - */ -class UniverseScope extends @universescope, Scope { - override string toString() { result = "universe scope" } -} - -/** A package scope. */ -class PackageScope extends @packagescope, Scope { - /** Gets the package whose scope this is. */ - Package getPackage() { this = result.getScope() } - - override string toString() { result = "package scope" } -} - -/** A local scope. */ -class LocalScope extends @localscope, Scope, Locatable { - /** Gets the AST node inducing this scope. */ - ScopeNode getNode() { this = result.getScope() } - - /** - * Gets the function scope in which this scope is nested. - * - * For function scopes, this is the scope itself. - */ - FunctionScope getEnclosingFunctionScope() { - result = this.getOuterScope().(LocalScope).getEnclosingFunctionScope() - } - - override string toString() { result = "local scope" } -} - -/** A local scope induced by a file. */ -class FileScope extends LocalScope { - FileScope() { this.getNode() instanceof File } -} - -/** A local scope induced by a function definition. */ -class FunctionScope extends LocalScope { - FuncDef f; - - FunctionScope() { this.getNode() = f.getTypeExpr() } - - /** Gets the function inducing this scope. */ - FuncDef getFunction() { result = f } - - override FunctionScope getEnclosingFunctionScope() { result = this } - - override string toString() { result = "function scope" } -} - -/** - * A declared or built-in entity (that is, package, type, constant, variable, function or label) - */ -class Entity extends @object { - /** - * Gets the name of this entity. - * - * Anonymous entities (such as the receiver variables of interface methods) have the empty string as their name. - */ - string getName() { objects(this, _, result) } - - /** Gets the package in which this entity is declared, if any. */ - Package getPackage() { result.getScope() = this.getScope() } - - /** Holds if this entity is declared in a package with path `pkg` and has the given `name`. */ - predicate hasQualifiedName(string pkg, string name) { - pkg = this.getPackage().getPath() and - name = this.getName() - } - - /** Gets the qualified name of this entity, if any. */ - string getQualifiedName() { - exists(string pkg, string name | this.hasQualifiedName(pkg, name) | result = pkg + "." + name) - } - - /** - * Gets the scope in which this entity is declared, if any. - * - * Entities corresponding to fields and methods do not have a scope. - */ - Scope getScope() { objectscopes(this, result) } - - /** Gets the declaring identifier for this entity. */ - Ident getDeclaration() { result.declares(this) } - - /** Gets a reference to this entity. */ - Name getAReference() { result.getTarget() = this } - - /** Gets the type of this entity. */ - Type getType() { objecttypes(this, result) } - - /** Gets a textual representation of this entity. */ - string toString() { result = this.getName() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [LGTM locations](https://lgtm.com/help/ql/locations). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - // take the location of the declaration if there is one - this.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - // otherwise fall back on dummy location - not exists(this.getDeclaration()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } -} - -/** A declared entity (that is, type, constant, variable or function). */ -class DeclaredEntity extends Entity, @declobject { - /** Gets the expression to which this entity is initialized, if any. */ - Expr getInit() { - exists(ValueSpec spec, int i | - spec.getNameExpr(i) = this.getDeclaration() and - spec.getInit(i) = result - ) - } -} - -/** A built-in entity (that is, type, constant or function). */ -class BuiltinEntity extends Entity, @builtinobject { } - -/** An imported package. */ -class PackageEntity extends Entity, @pkgobject { } - -/** A built-in or declared named type. */ -class TypeEntity extends Entity, @typeobject { } - -/** A declared named type. */ -class DeclaredType extends TypeEntity, DeclaredEntity, @decltypeobject { - /** Gets the declaration specifier declaring this type. */ - TypeSpec getSpec() { result.getNameExpr() = this.getDeclaration() } -} - -/** A built-in named type. */ -class BuiltinType extends TypeEntity, BuiltinEntity, @builtintypeobject { } - -/** A built-in or declared constant, variable, field, method or function. */ -class ValueEntity extends Entity, @valueobject { - /** Gets a data-flow node that reads the value of this entity. */ - Read getARead() { result.reads(this) } - - /** Gets a control-flow node that updates the value of this entity. */ - Write getAWrite() { result.writes(this, _) } -} - -/** A built-in or declared constant. */ -class Constant extends ValueEntity, @constobject { } - -/** A declared constant. */ -class DeclaredConstant extends Constant, DeclaredEntity, @declconstobject { - /** Gets the declaration specifier declaring this constant. */ - ValueSpec getSpec() { result.getANameExpr() = this.getDeclaration() } -} - -/** A built-in constant. */ -class BuiltinConstant extends Constant, BuiltinEntity, @builtinconstobject { } - -/** - * A built-in or declared variable. - * - * Note that Go currently does not have any built-in variables, so this class is effectively - * an alias for `DeclaredVariable`. - */ -class Variable extends ValueEntity, @varobject { } - -/** A declared variable. */ -class DeclaredVariable extends Variable, DeclaredEntity, @declvarobject { - /** Gets the declaration specifier declaring this variable. */ - ValueSpec getSpec() { result.getANameExpr() = this.getDeclaration() } -} - -/** A variable declared in a local scope (as opposed to a package scope or the universal scope). */ -class LocalVariable extends DeclaredVariable { - LocalVariable() { this.getScope() instanceof LocalScope } - - /** Gets the innermost function containing the scope of this variable, if any. */ - FuncDef getDeclaringFunction() { - result = this.getScope().(LocalScope).getEnclosingFunctionScope().getFunction() - } - - /** Holds if this variable is referenced inside a nested function. */ - predicate isCaptured() { - this.getDeclaringFunction() != this.getAReference().getEnclosingFunction() - } -} - -/** - * A (named) function parameter. - * - * Note that receiver variables are considered parameters. - */ -class Parameter extends DeclaredVariable { - FuncDef f; - int index; - - Parameter() { - f.(MethodDecl).getReceiverDecl().getNameExpr() = this.getDeclaration() and - index = -1 - or - exists(FuncTypeExpr tp | tp = f.getTypeExpr() | - this = - rank[index + 1](DeclaredVariable parm, int j, int k | - parm.getDeclaration() = tp.getParameterDecl(j).getNameExpr(k) - | - parm order by j, k - ) - ) - } - - /** Gets the function to which this parameter belongs. */ - FuncDef getFunction() { result = f } - - /** - * Gets the index of this parameter among all parameters of the function. - * - * The receiver is considered to have index -1. - */ - int getIndex() { result = index } - - /** Holds if this is the `i`th parameter of function `fd`. */ - predicate isParameterOf(FuncDef fd, int i) { fd = f and i = index } -} - -/** The receiver variable of a method. */ -class ReceiverVariable extends Parameter { - override MethodDecl f; - - ReceiverVariable() { index = -1 } - - /** Holds if this is the receiver variable of method `m`. */ - predicate isReceiverOf(MethodDecl m) { m = f } -} - -/** A (named) function result variable. */ -class ResultVariable extends DeclaredVariable { - FuncDef f; - int index; - - ResultVariable() { - exists(FuncTypeExpr tp | tp = f.getTypeExpr() | - this = - rank[index + 1](DeclaredVariable parm, int j, int k | - parm.getDeclaration() = tp.getResultDecl(j).getNameExpr(k) - | - parm order by j, k - ) - ) - } - - /** Gets the function to which this result variable belongs. */ - FuncDef getFunction() { result = f } - - /** Gets the index of this result among all results of the function. */ - int getIndex() { result = index } - - /** Holds if this is the `i`th result of function `fd`. */ - predicate isResultOf(FuncDef fd, int i) { fd = f and i = index } -} - -/** - * A struct field. - * - * Note that field identity is determined by type identity: if two struct types are identical in - * the sense of the Go language specification (https://golang.org/ref/spec#Type_identity), then - * any of their fields that have the same name are also identical. This, in turn, means that a - * field can have two or more declarations. - * - * For example, consider the following two type declarations: - * - * ```go - * type T1 struct { x int } - * type T2 struct { x int } - * ``` - * - * Types `T1` and `T2` are different, but their underlying struct types are identical. Hence - * the two declarations of `x` refer to the same field. - */ -class Field extends Variable { - StructType declaringType; - - Field() { fieldstructs(this, declaringType) } - - /** Gets the struct type declaring this field. */ - StructType getDeclaringType() { result = declaringType } - - override Package getPackage() { - exists(Type tp | tp.getUnderlyingType() = declaringType | result = tp.getPackage()) - } - - /** - * Holds if this field has name `f` and it belongs to a type with qualified name `tp`. - * - * Note that due to field embedding the same field may have multiple qualified names. - */ - override predicate hasQualifiedName(string tp, string f) { - exists(Type base | - tp = base.getQualifiedName() and - this = base.getField(f) - ) - } - - /** - * Holds if this field has name `f` and it belongs to a type `tp` declared in package `pkg`. - * - * Note that due to field embedding the same field may belong to multiple types. - */ - predicate hasQualifiedName(string pkg, string tp, string f) { - exists(Type base | - base.hasQualifiedName(pkg, tp) and - this = base.getField(f) - ) - } -} - -/** - * A field that belongs to a struct that may be embedded within another struct. - * - * When a selector addresses such a field, it is possible it is implicitly addressing a nested struct. - */ -class PromotedField extends Field { - PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) } -} - -/** A built-in or declared function. */ -class Function extends ValueEntity, @functionobject { - /** - * Gets a call to this function. - * - * This includes calls that target this function indirectly, by calling an - * interface method that this function implements. - */ - pragma[nomagic] - DataFlow::CallNode getACall() { this = result.getACalleeIncludingExternals().asFunction() } - - /** Gets the declaration of this function, if any. */ - FuncDecl getFuncDecl() { none() } - - /** Holds if this function is variadic. */ - predicate isVariadic() { none() } - - /** Holds if this function has no observable side effects. */ - predicate mayHaveSideEffects() { none() } - - /** - * Holds if this function may return without panicking, exiting the process, or looping forever. - * - * This predicate is an over-approximation: it may hold for functions that can never - * return normally, but it never fails to hold for functions that can. - * - * Note this is declared here and not in `DeclaredFunction` so that library models can override this - * by extending `Function` rather than having to remember to extend `DeclaredFunction`. - */ - predicate mayReturnNormally() { - not this.mustPanic() and - (ControlFlow::mayReturnNormally(this.getFuncDecl()) or not exists(this.getBody())) - } - - /** - * Holds if calling this function may cause a runtime panic. - * - * This predicate is an over-approximation: it may hold for functions that can never - * cause a runtime panic, but it never fails to hold for functions that can. - */ - predicate mayPanic() { any() } - - /** - * Holds if calling this function always causes a runtime panic. - * - * This predicate is an over-approximation: it may not hold for functions that do - * cause a runtime panic, but it never holds for functions that do not. - */ - predicate mustPanic() { none() } - - /** Gets the number of parameters of this function. */ - int getNumParameter() { result = this.getType().(SignatureType).getNumParameter() } - - /** Gets the type of the `i`th parameter of this function. */ - Type getParameterType(int i) { result = this.getType().(SignatureType).getParameterType(i) } - - /** Gets the number of results of this function. */ - int getNumResult() { result = this.getType().(SignatureType).getNumResult() } - - /** Gets the type of the `i`th result of this function. */ - Type getResultType(int i) { result = this.getType().(SignatureType).getResultType(i) } - - /** Gets the body of this function, if any. */ - BlockStmt getBody() { result = this.getFuncDecl().getBody() } - - /** Gets the `i`th parameter of this function. */ - Parameter getParameter(int i) { result.isParameterOf(this.getFuncDecl(), i) } - - /** Gets a parameter of this function. */ - Parameter getAParameter() { result = this.getParameter(_) } - - /** Gets the `i`th reslt variable of this function. */ - ResultVariable getResult(int i) { result.isResultOf(this.getFuncDecl(), i) } - - /** Gets a result variable of this function. */ - ResultVariable getAResult() { result = this.getResult(_) } -} - -/** - * A method, that is, a function with a receiver variable, or a function declared in an interface. - * - * Note that method identity is determined by receiver type identity: if two methods have the same - * name and their receiver types are identical in the sense of the Go language specification - * (https://golang.org/ref/spec#Type_identity), then the two methods are identical as well. - */ -class Method extends Function { - Variable receiver; - - Method() { methodreceivers(this, receiver) } - - override Package getPackage() { - // a method doesn't have a scope, so manually associate it with its receiver's - // package. - result = this.getReceiverType().getPackage() - } - - /** Holds if this method is declared in an interface. */ - predicate isInterfaceMethod() { - this.getReceiverType().getUnderlyingType() instanceof InterfaceType - } - - /** Gets the receiver variable of this method. */ - Variable getReceiver() { result = receiver } - - /** Gets the type of the receiver variable of this method. */ - Type getReceiverType() { result = receiver.getType() } - - /** - * Gets the receiver base type of this method, that is, either the base type of the receiver type - * if it is a pointer type, or the receiver type itself if it is not a pointer type. - */ - Type getReceiverBaseType() { - exists(Type recv | recv = this.getReceiverType() | - if recv instanceof PointerType - then result = recv.(PointerType).getBaseType() - else result = recv - ) - } - - /** Holds if this method has name `m` and belongs to the method set of type `tp` or `*tp`. */ - private predicate isIn(NamedType tp, string m) { - this = tp.getMethod(m) or - this = tp.getPointerType().getMethod(m) - } - - /** - * Holds if this method has name `m` and belongs to the method set of a type `T` or `*T` where - * `T` has qualified name `tp`. - * - * Note that `meth.hasQualifiedName(tp, m)` is almost, but not quite, equivalent to - * `exists(Type t | tp = t.getQualifiedName() and meth = t.getMethod(m))`: the latter - * distinguishes between the method sets of `T` and `*T`, while the former does not. - */ - override predicate hasQualifiedName(string tp, string m) { - exists(NamedType t | - this.isIn(t, m) and - tp = t.getQualifiedName() - ) - } - - /** - * Holds if this method has name `m` and belongs to the method set of a type `T` or `*T` where - * `T` is declared in package `pkg` and has name `tp`. - * - * Note that `meth.hasQualifiedName(pkg, tp, m)` is almost, but not quite, equivalent to - * `exists(Type t | t.hasQualifiedName(pkg, tp) and meth = t.getMethod(m))`: the latter - * distinguishes between the method sets of `T` and `*T`, while the former does not. - */ - predicate hasQualifiedName(string pkg, string tp, string m) { - exists(NamedType t | - this.isIn(t, m) and - t.hasQualifiedName(pkg, tp) - ) - } - - /** - * Holds if this method implements the method `m`, that is, if `m` is a method - * on an interface, and this is a method with the same name on a type that - * implements that interface. - * - * Note that all methods implement themselves, and interface methods _only_ - * implement themselves. - */ - predicate implements(Method m) { - this = m - or - not this.isInterfaceMethod() and - exists(Type t | - this = t.getMethod(m.getName()) and - t.implements(m.getReceiverType().getUnderlyingType()) - ) - } - - /** - * Holds if this method implements the method that has qualified name `pkg.tp.name`, that is, if - * `pkg.tp.name` is a method on an interface, and this is a method with the same name on a type - * that implements that interface. - */ - predicate implements(string pkg, string tp, string name) { - exists(Method m | m.hasQualifiedName(pkg, tp, name) | this.implements(m)) - } -} - -/** - * A method whose receiver may be embedded within a struct. - * - * When a selector addresses such a method, it is possible it is implicitly addressing a nested struct. - */ -class PromotedMethod extends Method { - PromotedMethod() { this = any(StructType t).getMethodOfEmbedded(_, _, _) } -} - -/** A declared function. */ -class DeclaredFunction extends Function, DeclaredEntity, @declfunctionobject { - override FuncDecl getFuncDecl() { result.getNameExpr() = this.getDeclaration() } - - override predicate mayHaveSideEffects() { - not exists(this.getBody()) - or - exists(BlockStmt body | body = this.getBody() | - body.mayHaveSideEffects() - or - // functions declared in files with build constraints may be defined differently - // for different platforms, so allow them to avoid false positives - body.getFile().hasBuildConstraints() - ) - } - - override predicate isVariadic() { this.getType().(SignatureType).isVariadic() } -} - -/** A built-in function. */ -class BuiltinFunction extends Function, BuiltinEntity, @builtinfunctionobject { - override predicate mayHaveSideEffects() { builtinFunction(this.getName(), false, _, _, _) } - - override predicate mayPanic() { builtinFunction(this.getName(), _, true, _, _) } - - override predicate mustPanic() { builtinFunction(this.getName(), _, _, true, _) } - - override predicate isVariadic() { builtinFunction(this.getName(), _, _, _, true) } - - /** - * Holds if this function is pure, that is, it has no observable side effects and - * no non-determinism. - */ - predicate isPure() { not this.mayHaveSideEffects() } -} - -private newtype TCallable = - TFunctionCallable(Function f) or - TFuncLitCallable(FuncLit l) - -/** - * This is either a `Function` or a `FuncLit`, because of limitations of both - * `Function` and `FuncDef`: - * - `Function` is an entity, and therefore does not include function literals, and - * - `FuncDef` is an AST node, and so is not extracted for functions from external libraries. - */ -class Callable extends TCallable { - /** Gets a textual representation of this callable. */ - string toString() { result = [this.asFunction().toString(), this.asFuncLit().toString()] } - - /** Gets this callable as a function, if it is one. */ - Function asFunction() { this = TFunctionCallable(result) } - - /** Gets this callable as a function literal, if it is one. */ - FuncLit asFuncLit() { this = TFuncLitCallable(result) } - - /** Gets this function's definition, if it exists. */ - FuncDef getFuncDef() { result = [this.asFuncLit().(FuncDef), this.asFunction().getFuncDecl()] } - - /** Gets the type of this callable. */ - SignatureType getType() { - result = this.asFunction().getType() or - result = this.asFuncLit().getType() - } - - /** Gets the name of this callable. */ - string getName() { - result = this.asFunction().getName() or - result = this.asFuncLit().getName() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `sc` of line `sl` to - * column `ec` of line `el` in file `fp`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) { - this.asFunction().hasLocationInfo(fp, sl, sc, el, ec) or - this.asFuncLit().hasLocationInfo(fp, sl, sc, el, ec) - } -} - -/** A statement label. */ -class Label extends Entity, @labelobject { } - -/** - * Holds if `name` is a built-in function, where - * - * - `pure` is true if the function has no observable side effects, and false otherwise; - * - `mayPanic` is true if calling this function may cause a panic, and false otherwise; - * - `mustPanic` is true if calling this function always causes a panic, and false otherwise; - * - `variadic` is true if this function is variadic, and false otherwise. - * - * Allocating memory is not considered an observable side effect. - */ -private predicate builtinFunction( - string name, boolean pure, boolean mayPanic, boolean mustPanic, boolean variadic -) { - name = "append" and pure = false and mayPanic = false and mustPanic = false and variadic = true - or - name = "cap" and pure = true and mayPanic = false and mustPanic = false and variadic = false - or - name = "close" and pure = false and mayPanic = true and mustPanic = false and variadic = false - or - name = "complex" and pure = true and mayPanic = true and mustPanic = false and variadic = false - or - name = "copy" and pure = false and mayPanic = true and mustPanic = false and variadic = false - or - name = "delete" and pure = false and mayPanic = false and mustPanic = false and variadic = false - or - name = "imag" and pure = true and mayPanic = false and mustPanic = false and variadic = false - or - name = "len" and pure = true and mayPanic = false and mustPanic = false and variadic = false - or - name = "make" and pure = true and mayPanic = true and mustPanic = false and variadic = true - or - name = "new" and pure = true and mayPanic = false and mustPanic = false and variadic = false - or - name = "panic" and pure = false and mayPanic = true and mustPanic = true and variadic = false - or - name = "print" and pure = false and mayPanic = false and mustPanic = false and variadic = true - or - name = "println" and pure = false and mayPanic = false and mustPanic = false and variadic = true - or - name = "real" and pure = true and mayPanic = false and mustPanic = false and variadic = false - or - name = "recover" and pure = false and mayPanic = false and mustPanic = false and variadic = false -} - -/** Provides helper predicates for working with built-in objects from the universe scope. */ -module Builtin { - // built-in types - /** Gets the built-in type `bool`. */ - BuiltinType bool() { result.getName() = "bool" } - - /** Gets the built-in type `byte`. */ - BuiltinType byte() { result.getName() = "byte" } - - /** Gets the built-in type `complex64`. */ - BuiltinType complex64() { result.getName() = "complex64" } - - /** Gets the built-in type `complex128`. */ - BuiltinType complex128() { result.getName() = "complex128" } - - /** Gets the built-in type `error`. */ - BuiltinType error() { result.getName() = "error" } - - /** Gets the built-in type `float32`. */ - BuiltinType float32() { result.getName() = "float32" } - - /** Gets the built-in type `float64`. */ - BuiltinType float64() { result.getName() = "float64" } - - /** Gets the built-in type `int`. */ - BuiltinType int_() { result.getName() = "int" } - - /** Gets the built-in type `int8`. */ - BuiltinType int8() { result.getName() = "int8" } - - /** Gets the built-in type `int16`. */ - BuiltinType int16() { result.getName() = "int16" } - - /** Gets the built-in type `int32`. */ - BuiltinType int32() { result.getName() = "int32" } - - /** Gets the built-in type `int64`. */ - BuiltinType int64() { result.getName() = "int64" } - - /** Gets the built-in type `rune`. */ - BuiltinType rune() { result.getName() = "rune" } - - /** Gets the built-in type `string`. */ - BuiltinType string_() { result.getName() = "string" } - - /** Gets the built-in type `uint`. */ - BuiltinType uint() { result.getName() = "uint" } - - /** Gets the built-in type `uint8`. */ - BuiltinType uint8() { result.getName() = "uint8" } - - /** Gets the built-in type `uint16`. */ - BuiltinType uint16() { result.getName() = "uint16" } - - /** Gets the built-in type `uint32`. */ - BuiltinType uint32() { result.getName() = "uint32" } - - /** Gets the built-in type `uint64`. */ - BuiltinType uint64() { result.getName() = "uint64" } - - /** Gets the built-in type `uintptr`. */ - BuiltinType uintptr() { result.getName() = "uintptr" } - - // built-in constants - /** Gets the built-in constant `true`. */ - BuiltinConstant true_() { result.getName() = "true" } - - /** Gets the built-in constant `false`. */ - BuiltinConstant false_() { result.getName() = "false" } - - /** Gets the built-in constant corresponding to `b`. */ - BuiltinConstant bool(boolean b) { - b = true and result = true_() - or - b = false and result = false_() - } - - /** Gets the built-in constant `iota`. */ - BuiltinConstant iota() { result.getName() = "iota" } - - // built-in zero value - /** Gets the built-in zero-value `nil`. */ - BuiltinConstant nil() { result.getName() = "nil" } - - /** Gets the built-in function `append`. */ - BuiltinFunction append() { result.getName() = "append" } - - /** Gets the built-in function `cap`. */ - BuiltinFunction cap() { result.getName() = "cap" } - - /** Gets the built-in function `close`. */ - BuiltinFunction close() { result.getName() = "close" } - - /** Gets the built-in function `complex`. */ - BuiltinFunction complex() { result.getName() = "complex" } - - /** Gets the built-in function `copy`. */ - BuiltinFunction copy() { result.getName() = "copy" } - - /** Gets the built-in function `delete`. */ - BuiltinFunction delete() { result.getName() = "delete" } - - /** Gets the built-in function `imag`. */ - BuiltinFunction imag() { result.getName() = "imag" } - - /** Gets the built-in function `len`. */ - BuiltinFunction len() { result.getName() = "len" } - - /** Gets the built-in function `make`. */ - BuiltinFunction make() { result.getName() = "make" } - - /** Gets the built-in function `new`. */ - BuiltinFunction new() { result.getName() = "new" } - - /** Gets the built-in function `panic`. */ - BuiltinFunction panic() { result.getName() = "panic" } - - /** Gets the built-in function `print`. */ - BuiltinFunction print() { result.getName() = "print" } - - /** Gets the built-in function `println`. */ - BuiltinFunction println() { result.getName() = "println" } - - /** Gets the built-in function `real`. */ - BuiltinFunction real() { result.getName() = "real" } - - /** Gets the built-in function `recover`. */ - BuiltinFunction recover() { result.getName() = "recover" } -} diff --git a/ql/lib/semmle/go/Stmt.qll b/ql/lib/semmle/go/Stmt.qll deleted file mode 100644 index 9873bf1db..000000000 --- a/ql/lib/semmle/go/Stmt.qll +++ /dev/null @@ -1,1135 +0,0 @@ -/** - * Provides classes for working with statements. - */ - -import go - -/** - * A statement. - * - * Examples: - * - * ```go - * a = 0 - * - * if x := f(); x < y { - * return y - x - * } else { - * return x - y - * } - * ``` - */ -class Stmt extends @stmt, ExprParent, StmtParent { - /** - * Gets the kind of this statement, which is an integer value representing the statement's - * node type. - * - * Note that the mapping from node types to integer kinds is considered an implementation detail - * and subject to change without notice. - */ - int getKind() { stmts(this, result, _, _) } - - /** - * Holds if the execution of this statement may produce observable side effects. - * - * Memory allocation is not considered an observable side effect. - */ - predicate mayHaveSideEffects() { none() } - - /** Gets the first control-flow node in this statement. */ - ControlFlow::Node getFirstControlFlowNode() { result.isFirstNodeOf(this) } -} - -/** - * A bad statement, that is, a statement that could not be parsed. - * - * Examples: - * - * ```go - * go fmt.Println - * defer int - * ``` - */ -class BadStmt extends @badstmt, Stmt { - override string toString() { result = "bad statement" } - - override string getAPrimaryQlClass() { result = "BadStmt" } -} - -/** - * A declaration statement. - * - * Examples: - * - * ```go - * var i int - * const pi = 3.14159 - * type Printer interface{ Print() } - * ``` - */ -class DeclStmt extends @declstmt, Stmt, DeclParent { - /** Gets the declaration in this statement. */ - Decl getDecl() { result = getDecl(0) } - - override predicate mayHaveSideEffects() { getDecl().mayHaveSideEffects() } - - override string toString() { result = "declaration statement" } - - override string getAPrimaryQlClass() { result = "DeclStmt" } -} - -/** - * An empty statement. - * - * Examples: - * - * ```go - * ; - * ``` - */ -class EmptyStmt extends @emptystmt, Stmt { - override string toString() { result = "empty statement" } - - override string getAPrimaryQlClass() { result = "EmptyStmt" } -} - -/** - * A labeled statement. - * - * Examples: - * - * ```go - * Error: log.Panic("error encountered") - * ``` - */ -class LabeledStmt extends @labeledstmt, Stmt { - /** Gets the identifier representing the label. */ - Ident getLabelExpr() { result = getChildExpr(0) } - - /** Gets the label. */ - string getLabel() { result = getLabelExpr().getName() } - - /** Gets the statement that is being labeled. */ - Stmt getStmt() { result = getChildStmt(1) } - - override predicate mayHaveSideEffects() { getStmt().mayHaveSideEffects() } - - override string toString() { result = "labeled statement" } - - override string getAPrimaryQlClass() { result = "LabeledStmt" } -} - -/** - * An expression statement. - * - * Examples: - * - * ```go - * h(x+y) - * f.Close() - * <-ch - * (<-ch) - * ``` - */ -class ExprStmt extends @exprstmt, Stmt { - /** Gets the expression. */ - Expr getExpr() { result = getChildExpr(0) } - - override predicate mayHaveSideEffects() { getExpr().mayHaveSideEffects() } - - override string toString() { result = "expression statement" } - - override string getAPrimaryQlClass() { result = "ExprStmt" } -} - -/** - * A send statement. - * - * Examples: - * - * ```go - * ch <- 3 - * ``` - */ -class SendStmt extends @sendstmt, Stmt { - /** Gets the expression representing the channel. */ - Expr getChannel() { result = getChildExpr(0) } - - /** Gets the expression representing the value being sent. */ - Expr getValue() { result = getChildExpr(1) } - - override predicate mayHaveSideEffects() { any() } - - override string toString() { result = "send statement" } - - override string getAPrimaryQlClass() { result = "SendStmt" } -} - -/** - * An increment or decrement statement. - * - * Examples: - * - * ```go - * a++ - * b-- - * ``` - */ -class IncDecStmt extends @incdecstmt, Stmt { - /** Gets the expression being incremented or decremented. */ - Expr getOperand() { result = getChildExpr(0) } - - /** Gets the increment or decrement operator. */ - string getOperator() { none() } - - override predicate mayHaveSideEffects() { any() } -} - -/** - * An increment statement. - * - * Examples: - * - * ```go - * a++ - * ``` - */ -class IncStmt extends @incstmt, IncDecStmt { - override string getOperator() { result = "++" } - - override string toString() { result = "increment statement" } - - override string getAPrimaryQlClass() { result = "IncStmt" } -} - -/** - * A decrement statement. - * - * Examples: - * - * ```go - * b-- - * ``` - */ -class DecStmt extends @decstmt, IncDecStmt { - override string getOperator() { result = "--" } - - override string toString() { result = "decrement statement" } - - override string getAPrimaryQlClass() { result = "DecStmt" } -} - -/** - * A (simple or compound) assignment statement. - * - * Examples: - * - * ```go - * x := 1 - * *p = f() - * a[i] = 23 - * (k) = <-ch // same as: k = <-ch - * a += 2 - * ``` - */ -class Assignment extends @assignment, Stmt { - /** Gets the `i`th left-hand side of this assignment (0-based). */ - Expr getLhs(int i) { - i >= 0 and - result = getChildExpr(-(i + 1)) - } - - /** Gets a left-hand side of this assignment. */ - Expr getAnLhs() { result = getLhs(_) } - - /** Gets the number of left-hand sides of this assignment. */ - int getNumLhs() { result = count(getAnLhs()) } - - /** Gets the unique left-hand side of this assignment, if there is only one. */ - Expr getLhs() { getNumLhs() = 1 and result = getLhs(0) } - - /** Gets the `i`th right-hand side of this assignment (0-based). */ - Expr getRhs(int i) { - i >= 0 and - result = getChildExpr(i + 1) - } - - /** Gets a right-hand side of this assignment. */ - Expr getAnRhs() { result = getRhs(_) } - - /** Gets the number of right-hand sides of this assignment. */ - int getNumRhs() { result = count(getAnRhs()) } - - /** Gets the unique right-hand side of this assignment, if there is only one. */ - Expr getRhs() { getNumRhs() = 1 and result = getRhs(0) } - - /** Holds if this assignment assigns `rhs` to `lhs`. */ - predicate assigns(Expr lhs, Expr rhs) { exists(int i | lhs = getLhs(i) and rhs = getRhs(i)) } - - /** Gets the assignment operator in this statement. */ - string getOperator() { none() } - - override predicate mayHaveSideEffects() { any() } - - override string toString() { result = "... " + getOperator() + " ..." } -} - -/** - * A simple assignment statement, that is, an assignment without a compound operator. - * - * Examples: - * - * ```go - * x := 1 - * *p = f() - * a[i] = 23 - * (k) = <-ch // same as: k = <-ch - * ``` - */ -class SimpleAssignStmt extends @simpleassignstmt, Assignment { - override string getAPrimaryQlClass() { result = "SimpleAssignStmt" } -} - -/** - * A plain assignment statement. - * - * Examples: - * - * ```go - * *p = f() - * a[i] = 23 - * (k) = <-ch // same as: k = <-ch - * ``` - */ -class AssignStmt extends @assignstmt, SimpleAssignStmt { - override string getOperator() { result = "=" } - - override string getAPrimaryQlClass() { result = "AssignStmt" } -} - -/** - * A define statement. - * - * Examples: - * - * ```go - * x := 1 - * ``` - */ -class DefineStmt extends @definestmt, SimpleAssignStmt { - override string getOperator() { result = ":=" } - - override string getAPrimaryQlClass() { result = "DefineStmt" } -} - -/** - * A compound assignment statement. - * - * Examples: - * - * ```go - * a += 2 - * a /= 2 - * ``` - */ -class CompoundAssignStmt extends @compoundassignstmt, Assignment { } - -/** - * An add-assign statement using `+=`. - * - * Examples: - * - * ```go - * a += 2 - * ``` - */ -class AddAssignStmt extends @addassignstmt, CompoundAssignStmt { - override string getOperator() { result = "+=" } - - override string getAPrimaryQlClass() { result = "AddAssignStmt" } -} - -/** - * A subtract-assign statement using `-=`. - * - * Examples: - * - * ```go - * a -= 2 - * ``` - */ -class SubAssignStmt extends @subassignstmt, CompoundAssignStmt { - override string getOperator() { result = "-=" } - - override string getAPrimaryQlClass() { result = "SubAssignStmt" } -} - -/** - * A multiply-assign statement using `*=`. - * - * Examples: - * - * ```go - * a *= 2 - * ``` - */ -class MulAssignStmt extends @mulassignstmt, CompoundAssignStmt { - override string getOperator() { result = "*=" } - - override string getAPrimaryQlClass() { result = "MulAssignStmt" } -} - -/** - * A divide-assign statement using `/=`. - * - * Examples: - * - * ```go - * a /= 2 - * ``` - */ -class QuoAssignStmt extends @quoassignstmt, CompoundAssignStmt { - override string getOperator() { result = "/=" } - - override string getAPrimaryQlClass() { result = "QuoAssignStmt" } -} - -class DivAssignStmt = QuoAssignStmt; - -/** - * A modulo-assign statement using `%=`. - * - * Examples: - * - * ```go - * a %= 2 - * ``` - */ -class RemAssignStmt extends @remassignstmt, CompoundAssignStmt { - override string getOperator() { result = "%=" } - - override string getAPrimaryQlClass() { result = "RemAssignStmt" } -} - -class ModAssignStmt = RemAssignStmt; - -/** - * An and-assign statement using `&=`. - * - * Examples: - * - * ```go - * a &= 2 - * ``` - */ -class AndAssignStmt extends @andassignstmt, CompoundAssignStmt { - override string getOperator() { result = "&=" } - - override string getAPrimaryQlClass() { result = "AndAssignStmt" } -} - -/** - * An or-assign statement using `|=`. - * - * Examples: - * - * ```go - * a |= 2 - * ``` - */ -class OrAssignStmt extends @orassignstmt, CompoundAssignStmt { - override string getOperator() { result = "|=" } - - override string getAPrimaryQlClass() { result = "OrAssignStmt" } -} - -/** - * An xor-assign statement using `^=`. - * - * Examples: - * - * ```go - * a ^= 2 - * ``` - */ -class XorAssignStmt extends @xorassignstmt, CompoundAssignStmt { - override string getOperator() { result = "^=" } - - override string getAPrimaryQlClass() { result = "XorAssignStmt" } -} - -/** - * A left-shift-assign statement using `<<=`. - * - * Examples: - * - * ```go - * a <<= 2 - * ``` - */ -class ShlAssignStmt extends @shlassignstmt, CompoundAssignStmt { - override string getOperator() { result = "<<=" } - - override string getAPrimaryQlClass() { result = "ShlAssignStmt" } -} - -class LShiftAssignStmt = ShlAssignStmt; - -/** - * A right-shift-assign statement using `>>=`. - * - * Examples: - * - * ```go - * a >>= 2 - * ``` - */ -class ShrAssignStmt extends @shrassignstmt, CompoundAssignStmt { - override string getOperator() { result = ">>=" } - - override string getAPrimaryQlClass() { result = "ShrAssignStmt" } -} - -class RShiftAssignStmt = ShrAssignStmt; - -/** - * An and-not-assign statement using `&^=`. - * - * Examples: - * - * ```go - * a &^= 2 - * ``` - */ -class AndNotAssignStmt extends @andnotassignstmt, CompoundAssignStmt { - override string getOperator() { result = "&^=" } - - override string getAPrimaryQlClass() { result = "AndNotAssignStmt" } -} - -/** - * A `go` statement. - * - * Examples: - * - * ```go - * go fillPixels(row) - * ``` - */ -class GoStmt extends @gostmt, Stmt { - /** Gets the call. */ - CallExpr getCall() { result = getChildExpr(0) } - - override predicate mayHaveSideEffects() { getCall().mayHaveSideEffects() } - - override string toString() { result = "go statement" } - - override string getAPrimaryQlClass() { result = "GoStmt" } -} - -/** - * A `defer` statement. - * - * Examples: - * - * ```go - * defer mutex.Unlock() - * ``` - */ -class DeferStmt extends @deferstmt, Stmt { - /** Gets the call being deferred. */ - CallExpr getCall() { result = getChildExpr(0) } - - override predicate mayHaveSideEffects() { getCall().mayHaveSideEffects() } - - override string toString() { result = "defer statement" } - - override string getAPrimaryQlClass() { result = "DeferStmt" } -} - -/** - * A `return` statement. - * - * Examples: - * - * ```go - * return x - * ``` - */ -class ReturnStmt extends @returnstmt, Stmt { - /** Gets the `i`th returned expression (0-based) */ - Expr getExpr(int i) { result = getChildExpr(i) } - - /** Gets a returned expression. */ - Expr getAnExpr() { result = getExpr(_) } - - /** Gets the number of returned expressions. */ - int getNumExpr() { result = count(getAnExpr()) } - - /** Gets the unique returned expression, if there is only one. */ - Expr getExpr() { getNumChild() = 1 and result = getExpr(0) } - - override predicate mayHaveSideEffects() { getExpr().mayHaveSideEffects() } - - override string toString() { result = "return statement" } - - override string getAPrimaryQlClass() { result = "ReturnStmt" } -} - -/** - * A branch statement, for example a `break` or `goto`. - * - * Examples: - * - * ```go - * break - * break OuterLoop - * continue - * continue RowLoop - * goto Error - * fallthrough - * ``` - */ -class BranchStmt extends @branchstmt, Stmt { - /** Gets the expression denoting the target label of the branch, if any. */ - Ident getLabelExpr() { result = getChildExpr(0) } - - /** Gets the target label of the branch, if any. */ - string getLabel() { result = getLabelExpr().getName() } -} - -/** - * A `break` statement. - * - * Examples: - * - * ```go - * break - * break OuterLoop - * ``` - */ -class BreakStmt extends @breakstmt, BranchStmt { - override string toString() { result = "break statement" } - - override string getAPrimaryQlClass() { result = "BreakStmt" } -} - -/** - * A `continue` statement. - * - * Examples: - * - * ```go - * continue - * continue RowLoop - * ``` - */ -class ContinueStmt extends @continuestmt, BranchStmt { - override string toString() { result = "continue statement" } - - override string getAPrimaryQlClass() { result = "ContinueStmt" } -} - -/** - * A `goto` statement. - * - * Examples: - * - * ```go - * goto Error - * ``` - */ -class GotoStmt extends @gotostmt, BranchStmt { - override string toString() { result = "goto statement" } - - override string getAPrimaryQlClass() { result = "GotoStmt" } -} - -/** - * A `fallthrough` statement. - * - * Examples: - * - * ```go - * fallthrough - * ``` - */ -class FallthroughStmt extends @fallthroughstmt, BranchStmt { - override string toString() { result = "fallthrough statement" } - - override string getAPrimaryQlClass() { result = "FallthroughStmt" } -} - -/** - * A block statement. - * - * Examples: - * - * ```go - * { - * fmt.Printf("iteration %d\n", i) - * f(i) - * } - * ``` - */ -class BlockStmt extends @blockstmt, Stmt, ScopeNode { - /** Gets the `i`th statement in this block (0-based). */ - Stmt getStmt(int i) { result = getChildStmt(i) } - - /** Gets a statement in this block. */ - Stmt getAStmt() { result = getAChildStmt() } - - /** Gets the number of statements in this block. */ - int getNumStmt() { result = getNumChildStmt() } - - override predicate mayHaveSideEffects() { getAStmt().mayHaveSideEffects() } - - override string toString() { result = "block statement" } - - override string getAPrimaryQlClass() { result = "BlockStmt" } -} - -/** - * An `if` statement. - * - * Examples: - * - * ```go - * if x := f(); x < y { - * return y - x - * } else { - * return x - y - * } - * ``` - */ -class IfStmt extends @ifstmt, Stmt, ScopeNode { - /** Gets the init statement of this `if` statement, if any. */ - Stmt getInit() { result = getChildStmt(0) } - - /** Gets the condition of this `if` statement. */ - Expr getCond() { result = getChildExpr(1) } - - /** Gets the "then" branch of this `if` statement. */ - BlockStmt getThen() { result = getChildStmt(2) } - - /** Gets the "else" branch of this `if` statement, if any. */ - Stmt getElse() { result = getChildStmt(3) } - - override predicate mayHaveSideEffects() { - getInit().mayHaveSideEffects() or - getCond().mayHaveSideEffects() or - getThen().mayHaveSideEffects() or - getElse().mayHaveSideEffects() - } - - override string toString() { result = "if statement" } - - override string getAPrimaryQlClass() { result = "IfStmt" } -} - -/** - * A `case` or `default` clause in a `switch` statement. - * - * Examples: - * - * ```go - * case 0, 1: - * a = 1 - * fallthrough - * - * default: - * b = 2 - * - * case func(int) float64: - * printFunction(i) - * ``` - */ -class CaseClause extends @caseclause, Stmt, ScopeNode { - /** Gets the `i`th expression of this `case` clause (0-based). */ - Expr getExpr(int i) { result = getChildExpr(-(i + 1)) } - - /** Gets an expression of this `case` clause. */ - Expr getAnExpr() { result = getAChildExpr() } - - /** Gets the number of expressions of this `case` clause. */ - int getNumExpr() { result = getNumChildExpr() } - - /** Gets the `i`th statement of this `case` clause (0-based). */ - Stmt getStmt(int i) { result = getChildStmt(i) } - - /** Gets a statement of this `case` clause. */ - Stmt getAStmt() { result = getAChildStmt() } - - /** Gets the number of statements of this `case` clause. */ - int getNumStmt() { result = getNumChildStmt() } - - override predicate mayHaveSideEffects() { - getAnExpr().mayHaveSideEffects() or - getAStmt().mayHaveSideEffects() - } - - override string toString() { result = "case clause" } - - override string getAPrimaryQlClass() { result = "CaseClause" } -} - -/** - * A `switch` statement, that is, either an expression switch or a type switch. - * - * Examples: - * - * ```go - * switch x := f(); x { - * case 0, 1: - * a = 1 - * fallthrough - * default: - * b = 2 - * } - * - * switch i := x.(type) { - * default: - * printString("don't know the type") - * case nil: - * printString("x is nil") - * case int: - * printInt(i) - * case func(int) float64: - * printFunction(i) - * } - * ``` - */ -class SwitchStmt extends @switchstmt, Stmt, ScopeNode { - /** Gets the init statement of this `switch` statement, if any. */ - Stmt getInit() { result = getChildStmt(0) } - - /** Gets the body of this `switch` statement. */ - BlockStmt getBody() { result = getChildStmt(2) } - - /** Gets the `i`th case clause of this `switch` statement (0-based). */ - CaseClause getCase(int i) { result = getBody().getStmt(i) } - - /** Gets a case clause of this `switch` statement. */ - CaseClause getACase() { result = getCase(_) } - - /** Gets the number of case clauses in this `switch` statement. */ - int getNumCase() { result = count(getACase()) } - - /** Gets the `i`th non-default case clause of this `switch` statement (0-based). */ - CaseClause getNonDefaultCase(int i) { - result = - rank[i + 1](CaseClause cc, int j | cc = getCase(j) and exists(cc.getExpr(_)) | cc order by j) - } - - /** Gets a non-default case clause of this `switch` statement. */ - CaseClause getANonDefaultCase() { result = getNonDefaultCase(_) } - - /** Gets the number of non-default case clauses in this `switch` statement. */ - int getNumNonDefaultCase() { result = count(getANonDefaultCase()) } - - /** Gets the default case clause of this `switch` statement, if any. */ - CaseClause getDefault() { result = getACase() and not exists(result.getExpr(_)) } -} - -/** - * An expression-switch statement. - * - * Examples: - * - * ```go - * switch x := f(); x { - * case 0, 1: - * a = 1 - * fallthrough - * default: - * b = 2 - * } - * ``` - */ -class ExpressionSwitchStmt extends @exprswitchstmt, SwitchStmt { - /** Gets the switch expression of this `switch` statement. */ - Expr getExpr() { result = getChildExpr(1) } - - override predicate mayHaveSideEffects() { - getInit().mayHaveSideEffects() or - getBody().mayHaveSideEffects() - } - - override string toString() { result = "expression-switch statement" } - - override string getAPrimaryQlClass() { result = "ExpressionSwitchStmt" } -} - -/** - * A type-switch statement. - * - * Examples: - * - * ```go - * switch i := x.(type) { - * default: - * printString("don't know the type") // type of i is type of x (interface{}) - * case nil: - * printString("x is nil") // type of i is type of x (interface{}) - * case int: - * printInt(i) // type of i is int - * case func(int) float64: - * printFunction(i) // type of i is func(int) float64 - * } - * ``` - */ -class TypeSwitchStmt extends @typeswitchstmt, SwitchStmt { - /** Gets the assign statement of this type-switch statement. */ - SimpleAssignStmt getAssign() { result = getChildStmt(1) } - - /** Gets the test statement of this type-switch statement. This is a `SimpleAssignStmt` or `ExprStmt`. */ - Stmt getTest() { result = getChildStmt(1) } - - /** Gets the expression whose type is examined by this `switch` statement. */ - Expr getExpr() { result = getAssign().getRhs() or result = getChildStmt(1).(ExprStmt).getExpr() } - - override predicate mayHaveSideEffects() { any() } - - override string toString() { result = "type-switch statement" } - - override string getAPrimaryQlClass() { result = "TypeSwitchStmt" } -} - -/** - * A comm clause, that is, a `case` or `default` clause in a `select` statement. - * - * Examples: - * - * ```go - * case i1 = <-c1: - * print("received ", i1, " from c1\n") - * - * case c2 <- i2: - * print("sent ", i2, " to c2\n") - * - * case i3, ok := (<-c3): // same as: i3, ok := <-c3 - * if ok { - * print("received ", i3, " from c3\n") - * } else { - * print("c3 is closed\n") - * } - * - * default: - * print("no communication\n") - * ``` - */ -class CommClause extends @commclause, Stmt, ScopeNode { - /** Gets the comm statement of this clause, if any. */ - Stmt getComm() { result = getChildStmt(0) } - - /** Gets the `i`th statement of this clause (0-based). */ - Stmt getStmt(int i) { i >= 0 and result = getChildStmt(i + 1) } - - /** Gets a statement of this clause. */ - Stmt getAStmt() { result = getStmt(_) } - - /** Gets the number of statements of this clause. */ - int getNumStmt() { result = count(getAStmt()) } - - override predicate mayHaveSideEffects() { getAStmt().mayHaveSideEffects() } - - override string toString() { result = "comm clause" } - - override string getAPrimaryQlClass() { result = "CommClause" } -} - -/** - * A receive statement in a comm clause. - * - * Examples: - * - * ```go - * i1 = <-c1 - * i3, ok := <-c3 - * i3, ok := (<-c3) - * ``` - */ -class RecvStmt extends Stmt { - RecvStmt() { this = any(CommClause cc).getComm() and not this instanceof SendStmt } - - /** Gets the `i`th left-hand-side expression of this receive statement, if any. */ - Expr getLhs(int i) { result = this.(Assignment).getLhs(i) } - - /** Gets the number of left-hand-side expressions of this receive statement. */ - int getNumLhs() { result = count(getLhs(_)) } - - /** Gets the receive expression of this receive statement. */ - RecvExpr getExpr() { - result = this.(ExprStmt).getExpr() or - result = this.(Assignment).getRhs() - } - - override string getAPrimaryQlClass() { result = "RecvStmt" } -} - -/** - * A `select` statement. - * - * Examples: - * - * ```go - * select { - * case i1 = <-c1: - * print("received ", i1, " from c1\n") - * case c2 <- i2: - * print("sent ", i2, " to c2\n") - * case i3, ok := (<-c3): // same as: i3, ok := <-c3 - * if ok { - * print("received ", i3, " from c3\n") - * } else { - * print("c3 is closed\n") - * } - * default: - * print("no communication\n") - * } - * ``` - */ -class SelectStmt extends @selectstmt, Stmt { - /** Gets the body of this `select` statement. */ - BlockStmt getBody() { result = getChildStmt(0) } - - /** - * Gets the `i`th comm clause (that is, `case` or `default` clause) in this `select` statement. - */ - CommClause getCommClause(int i) { result = getBody().getStmt(i) } - - /** - * Gets a comm clause in this `select` statement. - */ - CommClause getACommClause() { result = getCommClause(_) } - - /** Gets the `i`th `case` clause in this `select` statement. */ - CommClause getNonDefaultCommClause(int i) { - result = - rank[i + 1](CommClause cc, int j | - cc = getCommClause(j) and exists(cc.getComm()) - | - cc order by j - ) - } - - /** Gets the number of `case` clauses in this `select` statement. */ - int getNumNonDefaultCommClause() { result = count(getNonDefaultCommClause(_)) } - - /** Gets the `default` clause in this `select` statement, if any. */ - CommClause getDefaultCommClause() { - result = getCommClause(_) and - not exists(result.getComm()) - } - - override predicate mayHaveSideEffects() { any() } - - override string toString() { result = "select statement" } - - override string getAPrimaryQlClass() { result = "SelectStmt" } -} - -/** - * A loop, that is, either a `for` statement or a `range` statement. - * - * Examples: - * - * ```go - * for a < b { - * a *= 2 - * } - * - * for i := 0; i < 10; i++ { - * f(i) - * } - * - * for key, value := range mymap { - * fmt.Printf("mymap[%s] = %d\n", key, value) - * } - * ``` - */ -class LoopStmt extends @loopstmt, Stmt, ScopeNode { - /** Gets the body of this loop. */ - BlockStmt getBody() { none() } -} - -/** - * A `for` statement. - * - * Examples: - * - * ```go - * for a < b { - * a *= 2 - * } - * - * for i := 0; i < 10; i++ { - * f(i) - * } - * ``` - */ -class ForStmt extends @forstmt, LoopStmt { - /** Gets the init statement of this `for` statement, if any. */ - Stmt getInit() { result = getChildStmt(0) } - - /** Gets the condition of this `for` statement. */ - Expr getCond() { result = getChildExpr(1) } - - /** Gets the post statement of this `for` statement. */ - Stmt getPost() { result = getChildStmt(2) } - - override BlockStmt getBody() { result = getChildStmt(3) } - - override predicate mayHaveSideEffects() { - getInit().mayHaveSideEffects() or - getCond().mayHaveSideEffects() or - getPost().mayHaveSideEffects() or - getBody().mayHaveSideEffects() - } - - override string toString() { result = "for statement" } - - override string getAPrimaryQlClass() { result = "ForStmt" } -} - -/** - * A `range` statement. - * - * Examples: - * - * ```go - * for key, value := range mymap { - * fmt.Printf("mymap[%s] = %d\n", key, value) - * } - * - * for _, value = range array { - * fmt.Printf("array contains: %d\n", value) - * } - * - * for index, _ := range str { - * fmt.Printf("str[%d] = ?\n", index) - * } - * - * for value = range ch { - * fmt.Printf("value from channel: %d\n", value) - * } - * ``` - */ -class RangeStmt extends @rangestmt, LoopStmt { - /** Gets the expression denoting the key of this `range` statement. */ - Expr getKey() { result = getChildExpr(0) } - - /** Get the expression denoting the value of this `range` statement. */ - Expr getValue() { result = getChildExpr(1) } - - /** Gets the domain of this `range` statement. */ - Expr getDomain() { result = getChildExpr(2) } - - override BlockStmt getBody() { result = getChildStmt(3) } - - override predicate mayHaveSideEffects() { any() } - - override string toString() { result = "range statement" } - - override string getAPrimaryQlClass() { result = "RangeStmt" } -} diff --git a/ql/lib/semmle/go/StringOps.qll b/ql/lib/semmle/go/StringOps.qll deleted file mode 100644 index 70f83328c..000000000 --- a/ql/lib/semmle/go/StringOps.qll +++ /dev/null @@ -1,523 +0,0 @@ -/** - * Provides predicates and classes for working with string operations. - */ - -import go - -/** Provides predicates and classes for working with string operations. */ -module StringOps { - /** - * An expression that is equivalent to `strings.HasPrefix(A, B)` or `!strings.HasPrefix(A, B)`. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `StringOps::HasPrefix::Range` instead. - */ - class HasPrefix extends DataFlow::Node { - HasPrefix::Range range; - - HasPrefix() { range = this } - - /** - * Gets the `A` in `strings.HasPrefix(A, B)`. - */ - DataFlow::Node getBaseString() { result = range.getBaseString() } - - /** - * Gets the `B` in `strings.HasPrefix(A, B)`. - */ - DataFlow::Node getSubstring() { result = range.getSubstring() } - - /** - * Gets the polarity of the check. - * - * If the polarity is `false` the check returns `true` if the string does not start - * with the given substring. - */ - boolean getPolarity() { result = range.getPolarity() } - } - - class StartsWith = HasPrefix; - - /** Provides predicates and classes for working with prefix checks. */ - module HasPrefix { - /** - * An expression that is equivalent to `strings.HasPrefix(A, B)` or `!strings.HasPrefix(A, B)`. - * - * Extend this class to model new APIs. If you want to refine existing API models, extend - * `StringOps::HasPrefix` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the `A` in `strings.HasPrefix(A, B)`. - */ - abstract DataFlow::Node getBaseString(); - - /** - * Gets the `B` in `strings.HasPrefix(A, B)`. - */ - abstract DataFlow::Node getSubstring(); - - /** - * Gets the polarity of the check. - * - * If the polarity is `false` the check returns `true` if the string does not start - * with the given substring. - */ - boolean getPolarity() { result = true } - } - - /** - * An expression of the form `strings.HasPrefix(A, B)`. - */ - private class StringsHasPrefix extends Range, DataFlow::CallNode { - StringsHasPrefix() { this.getTarget().hasQualifiedName("strings", "HasPrefix") } - - override DataFlow::Node getBaseString() { result = this.getArgument(0) } - - override DataFlow::Node getSubstring() { result = this.getArgument(1) } - } - - /** - * Holds if `eq` is of the form `nd == 0` or `nd != 0`. - */ - pragma[noinline] - private predicate comparesToZero(DataFlow::EqualityTestNode eq, DataFlow::Node nd) { - exists(DataFlow::Node zero | - eq.hasOperands(globalValueNumber(nd).getANode(), zero) and - zero.getIntValue() = 0 - ) - } - - /** - * An expression of the form `strings.Index(A, B) == 0`. - */ - private class HasPrefix_IndexOfEquals extends Range, DataFlow::EqualityTestNode { - DataFlow::CallNode indexOf; - - HasPrefix_IndexOfEquals() { - comparesToZero(this, indexOf) and - indexOf.getTarget().hasQualifiedName("strings", "Index") - } - - override DataFlow::Node getBaseString() { result = indexOf.getArgument(0) } - - override DataFlow::Node getSubstring() { result = indexOf.getArgument(1) } - - override boolean getPolarity() { result = expr.getPolarity() } - } - - /** - * Holds if `eq` is of the form `str[0] == rhs` or `str[0] != rhs`. - */ - pragma[noinline] - private predicate comparesFirstCharacter( - DataFlow::EqualityTestNode eq, DataFlow::Node str, DataFlow::Node rhs - ) { - exists(DataFlow::ElementReadNode read | - eq.hasOperands(globalValueNumber(read).getANode(), rhs) and - str = read.getBase() and - str.getType().getUnderlyingType() instanceof StringType and - read.getIndex().getIntValue() = 0 - ) - } - - /** - * A comparison of the form `x[0] == 'k'` for some rune literal `k`. - */ - private class HasPrefix_FirstCharacter extends Range, DataFlow::EqualityTestNode { - DataFlow::Node base; - DataFlow::Node runeLiteral; - - HasPrefix_FirstCharacter() { comparesFirstCharacter(this, base, runeLiteral) } - - override DataFlow::Node getBaseString() { result = base } - - override DataFlow::Node getSubstring() { result = runeLiteral } - - override boolean getPolarity() { result = expr.getPolarity() } - } - - /** - * A comparison of the form `x[:len(y)] == y`. - */ - private class HasPrefix_Substring extends Range, DataFlow::EqualityTestNode { - DataFlow::SliceNode slice; - DataFlow::Node substring; - - HasPrefix_Substring() { - this.eq(_, slice, substring) and - slice.getLow().getIntValue() = 0 and - ( - exists(DataFlow::CallNode len | - len = Builtin::len().getACall() and - len.getArgument(0) = globalValueNumber(substring).getANode() and - slice.getHigh() = globalValueNumber(len).getANode() - ) - or - substring.getStringValue().length() = slice.getHigh().getIntValue() - ) - } - - override DataFlow::Node getBaseString() { result = slice.getBase() } - - override DataFlow::Node getSubstring() { result = substring } - - override boolean getPolarity() { result = expr.getPolarity() } - } - } - - /** Provides predicates and classes for working with Printf-style formatters. */ - module Formatting { - /** - * Gets a regular expression for matching simple format-string components, including flags, - * width and precision specifiers, not including explicit argument indices. - */ - pragma[noinline] - private string getFormatComponentRegex() { - exists( - string literal, string opt_flag, string width, string prec, string opt_width_and_prec, - string operator, string verb - | - literal = "([^%]|%%)+" and - opt_flag = "[-+ #0]?" and - width = "\\d+|\\*" and - prec = "\\.(\\d+|\\*)" and - opt_width_and_prec = "(" + width + ")?(" + prec + ")?" and - operator = "[bcdeEfFgGoOpqstTxXUv]" and - verb = "(%" + opt_flag + opt_width_and_prec + operator + ")" - | - result = "(" + literal + "|" + verb + ")" - ) - } - - /** - * A function that performs string formatting in the same manner as `fmt.Printf` etc. - */ - abstract class Range extends Function { - /** - * Gets the parameter index of the format string. - */ - abstract int getFormatStringIndex(); - - /** - * Gets the parameter index of the first parameter to be formatted. - */ - abstract int getFirstFormattedParameterIndex(); - } - - /** - * A call to a `fmt.Printf`-style string formatting function. - */ - class StringFormatCall extends DataFlow::CallNode { - string fmt; - Range f; - - StringFormatCall() { - this = f.getACall() and - fmt = this.getArgument(f.getFormatStringIndex()).getStringValue() and - fmt.regexpMatch(getFormatComponentRegex() + "*") - } - - /** - * Gets the `n`th component of this format string. - */ - string getComponent(int n) { result = fmt.regexpFind(getFormatComponentRegex(), n, _) } - - /** - * Gets the `n`th argument formatted by this format call, where `formatDirective` specifies how it will be formatted. - */ - DataFlow::Node getOperand(int n, string formatDirective) { - formatDirective = this.getComponent(n) and - formatDirective.charAt(0) = "%" and - formatDirective.charAt(1) != "%" and - result = this.getArgument((n / 2) + f.getFirstFormattedParameterIndex()) - } - } - } - - /** - * A data-flow node that performs string concatenation. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `StringOps::Concatenation::Range` instead. - */ - class Concatenation extends DataFlow::Node { - Concatenation::Range self; - - Concatenation() { this = self } - - /** - * Gets the `n`th operand of this string concatenation, if there is a data-flow node for it. - */ - DataFlow::Node getOperand(int n) { result = self.getOperand(n) } - - /** - * Gets the string value of the `n`th operand of this string concatenation, if it is a constant. - */ - string getOperandStringValue(int n) { result = self.getOperandStringValue(n) } - - /** - * Gets the number of operands of this string concatenation. - */ - int getNumOperand() { result = self.getNumOperand() } - } - - /** Provides predicates and classes for working with string concatenations. */ - module Concatenation { - /** - * A data-flow node that performs string concatenation. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `StringOps::Concatenation` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the `n`th operand of this string concatenation, if there is a data-flow node for it. - */ - abstract DataFlow::Node getOperand(int n); - - /** - * Gets the string value of the `n`th operand of this string concatenation, if it is - * a constant. - */ - string getOperandStringValue(int n) { result = this.getOperand(n).getStringValue() } - - /** - * Gets the number of operands of this string concatenation. - */ - int getNumOperand() { result = count(this.getOperand(_)) } - } - - /** A string concatenation using the `+` or `+=` operator. */ - private class PlusConcat extends Range, DataFlow::BinaryOperationNode { - PlusConcat() { - this.getType() instanceof StringType and - this.getOperator() = "+" - } - - override DataFlow::Node getOperand(int n) { - n = 0 and result = this.getLeftOperand() - or - n = 1 and result = this.getRightOperand() - } - } - - /** - * A call to `fmt.Sprintf`, considered as a string concatenation. - * - * Only calls with simple format strings (no `*` specifiers, no explicit argument indices) - * are supported. Such format strings can be viewed as sequences of alternating literal and - * non-literal components. A literal component contains no `%` characters except `%%` pairs, - * while a non-literal component consists of `%`, a verb, and possibly flags and specifiers. - * Each non-literal component consumes exactly one argument. - * - * Literal components give rise to concatenation operands that have a string value but no - * data-flow node; non-literal `%s` or `%v` components give rise to concatenation operands - * that do have an associated data-flow node but possibly no string value; any other non-literal - * components give rise to concatenation operands that have neither an associated data-flow - * node nor a string value. This is because verbs like `%q` perform additional string - * transformations that we cannot easily represent. - */ - private class SprintfConcat extends Range instanceof Formatting::StringFormatCall { - SprintfConcat() { this = any(Function f | f.hasQualifiedName("fmt", "Sprintf")).getACall() } - - override DataFlow::Node getOperand(int n) { - result = Formatting::StringFormatCall.super.getOperand(n, ["%s", "%v"]) - } - - override string getOperandStringValue(int n) { - result = Range.super.getOperandStringValue(n) - or - exists(string cmp | cmp = Formatting::StringFormatCall.super.getComponent(n) | - (cmp.charAt(0) != "%" or cmp.charAt(1) = "%") and - result = cmp.replaceAll("%%", "%") - ) - } - - override int getNumOperand() { - result = max(int i | exists(Formatting::StringFormatCall.super.getComponent(i))) + 1 - } - } - - /** - * Holds if `src` flows to `dst` through the `n`th operand of the given concatenation operator. - */ - predicate taintStep(DataFlow::Node src, DataFlow::Node dst, Concatenation cat, int n) { - src = cat.getOperand(n) and - dst = cat - } - - /** - * Holds if there is a taint step from `src` to `dst` through string concatenation. - */ - predicate taintStep(DataFlow::Node src, DataFlow::Node dst) { taintStep(src, dst, _, _) } - } - - private newtype TConcatenationElement = - /** A root concatenation element that is not itself an operand of a string concatenation. */ - MkConcatenationRoot(Concatenation cat) { not cat = any(Concatenation parent).getOperand(_) } or - /** A concatenation element that is an operand of a string concatenation. */ - MkConcatenationOperand(Concatenation parent, int i) { i in [0 .. parent.getNumOperand() - 1] } - - /** - * An element of a string concatenation, which either itself performs a string concatenation or - * occurs as an operand in a string concatenation. - * - * For example, the expression `x + y + z` contains the following concatenation - * elements: - * - * - The leaf elements `x`, `y`, and `z` - * - The intermediate element `x + y`, which is both a concatenation and an operand - * - The root element `x + y + z` - */ - class ConcatenationElement extends TConcatenationElement { - /** - * Gets the data-flow node corresponding to this concatenation element, if any. - */ - DataFlow::Node asNode() { - this = MkConcatenationRoot(result) - or - exists(Concatenation parent, int i | this = MkConcatenationOperand(parent, i) | - result = parent.getOperand(i) - ) - } - - /** - * Gets the string value of this concatenation element if it is a constant. - */ - string getStringValue() { - result = this.asNode().getStringValue() - or - exists(Concatenation parent, int i | this = MkConcatenationOperand(parent, i) | - result = parent.getOperandStringValue(i) - ) - } - - /** - * Gets the `n`th operand of this string concatenation. - */ - ConcatenationOperand getOperand(int n) { result = MkConcatenationOperand(this.asNode(), n) } - - /** - * Gets an operand of this string concatenation. - */ - ConcatenationOperand getAnOperand() { result = this.getOperand(_) } - - /** - * Gets the number of operands of this string concatenation. - */ - int getNumOperand() { result = count(this.getAnOperand()) } - - /** - * Gets the first operand of this string concatenation. - * - * For example, the first operand of `(x + y) + z` is `(x + y)`. - */ - ConcatenationOperand getFirstOperand() { result = this.getOperand(0) } - - /** - * Gets the last operand of this string concatenation. - * - * For example, the last operand of `x + (y + z)` is `(y + z)`. - */ - ConcatenationOperand getLastOperand() { result = this.getOperand(this.getNumOperand() - 1) } - - /** - * Gets the root of the concatenation tree to which this element belongs. - */ - ConcatenationRoot getConcatenationRoot() { this = result.getAnOperand*() } - - /** - * Gets a leaf in the concatenation tree that this element is the root of. - */ - ConcatenationLeaf getALeaf() { result = this.getAnOperand*() } - - /** - * Gets the first leaf in this concatenation tree. - * - * For example, the first leaf of `(x + y) + z` is `x`. - */ - ConcatenationLeaf getFirstLeaf() { result = this.getFirstOperand*() } - - /** - * Gets the last leaf in this concatenation tree. - * - * For example, the last leaf of `x + (y + z)` is `z`. - */ - ConcatenationLeaf getLastLeaf() { result = this.getLastOperand*() } - - /** Gets a textual representation of this concatenation element. */ - string toString() { - if exists(this.asNode()) - then result = this.asNode().toString() - else - if exists(this.getStringValue()) - then result = this.getStringValue() - else result = "concatenation element" - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - // use dummy location for elements that don't have a corresponding node - not exists(this.asNode()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - } - - /** - * One of the operands in a string concatenation. - * - * See `ConcatenationElement` for more information. - */ - class ConcatenationOperand extends ConcatenationElement, MkConcatenationOperand { } - - /** - * A data-flow node that performs a string concatenation, and is not an - * immediate operand in a larger string concatenation. - * - * See `ConcatenationElement` for more information. - */ - class ConcatenationRoot extends ConcatenationElement, MkConcatenationRoot { } - - /** - * An operand to a concatenation that is not itself a concatenation. - * - * See `ConcatenationElement` for more information. - */ - class ConcatenationLeaf extends ConcatenationOperand { - ConcatenationLeaf() { not exists(this.getAnOperand()) } - - /** - * Gets the operand immediately preceding this one in its parent concatenation. - * - * For example, in `(x + y) + z`, the previous leaf for `z` is `y`. - */ - ConcatenationLeaf getPreviousLeaf() { - exists(ConcatenationElement parent, int i | - result = parent.getOperand(i - 1).getLastLeaf() and - this = parent.getOperand(i).getFirstLeaf() - ) - } - - /** - * Gets the operand immediately succeeding this one in its parent concatenation. - * - * For example, in `(x + y) + z`, the previous leaf for `y` is `z`. - */ - ConcatenationLeaf getNextLeaf() { this = result.getPreviousLeaf() } - } -} diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll deleted file mode 100644 index 8ec3978e6..000000000 --- a/ql/lib/semmle/go/Types.qll +++ /dev/null @@ -1,1015 +0,0 @@ -/** - * Provides classes for working with Go types. - */ - -import go - -/** A Go type. */ -class Type extends @type { - /** Gets the name of this type, if it has one. */ - string getName() { typename(this, result) } - - /** - * Gets the underlying type of this type after any type aliases have been replaced - * with their definition. - */ - Type getUnderlyingType() { result = this } - - /** - * Gets the entity associated with this type. - */ - TypeEntity getEntity() { type_objects(this, result) } - - /** Gets the package in which this type is declared, if any. */ - Package getPackage() { result = this.getEntity().getPackage() } - - /** - * Gets the qualified name of this type, if any. - * - * Only (defined) named types like `io.Writer` have a qualified name. Basic types like `int`, - * pointer types like `*io.Writer`, and other composite types do not have a qualified name. - */ - string getQualifiedName() { result = this.getEntity().getQualifiedName() } - - /** - * Holds if this type is declared in a package with path `pkg` and has name `name`. - * - * Only (defined) named types like `io.Writer` have a qualified name. Basic types like `int`, - * pointer types like `*io.Writer`, and other composite types do not have a qualified name. - */ - predicate hasQualifiedName(string pkg, string name) { - this.getEntity().hasQualifiedName(pkg, name) - } - - /** - * Holds if the method set of this type contains a method named `m` of type `t`. - */ - predicate hasMethod(string m, SignatureType t) { t = this.getMethod(m).getType() } - - /** - * Gets the method `m` belonging to the method set of this type, if any. - * - * Note that this predicate never has a result for struct types. Methods are associated - * with the corresponding named type instead. - */ - Method getMethod(string m) { - result.getReceiverType() = this and - result.getName() = m - } - - /** - * Gets the field `f` of this type. - * - * This includes fields promoted from an embedded field. - */ - Field getField(string f) { result = this.getUnderlyingType().getField(f) } - - /** - * Holds if this type implements interface `i`, that is, the method set of `i` - * is contained in the method set of this type and any type restrictions are - * satisfied. - */ - predicate implements(InterfaceType i) { - if i = any(ComparableType comparable).getUnderlyingType() - then this.implementsComparable() - else this.implementsNotComparable(i) - } - - /** - * Holds if this type implements interface `i`, which is not the underlying - * type of `comparable`. This predicate is needed to avoid non-monotonic - * recursion. - */ - private predicate implementsNotComparable(InterfaceType i) { - ( - forall(TypeSetLiteralType tslit | tslit = i.getAnEmbeddedTypeSetLiteral() | - tslit.includesType(this) - ) and - ( - hasNoMethods(i) - or - this.hasMethod(getExampleMethodName(i), _) and - forall(string m, SignatureType t | i.hasMethod(m, t) | this.hasMethod(m, t)) - ) - ) - } - - /** - * Holds if this type implements `comparable`. This includes being - * `comparable` itself, or the underlying type of `comparable`. - */ - predicate implementsComparable() { - exists(Type u | u = this.getUnderlyingType() | - // Note that BasicType includes Invalidtype - u instanceof BasicType - or - u instanceof PointerType - or - u instanceof ChanType - or - u instanceof StructType and - forall(Type fieldtp | u.(StructType).hasField(_, fieldtp) | fieldtp.implementsComparable()) - or - u instanceof ArrayType and u.(ArrayType).getElementType().implementsComparable() - or - exists(InterfaceType uif | uif = u | - not uif instanceof BasicInterfaceType and - if exists(uif.getAnEmbeddedTypeSetLiteral()) - then - // All types in the intersection of all the embedded type set - // literals must implement comparable. - forall(Type intersectionType | - intersectionType = uif.getAnEmbeddedTypeSetLiteral().getATerm().getType() and - forall(TypeSetLiteralType tslit | tslit = uif.getAnEmbeddedTypeSetLiteral() | - intersectionType = tslit.getATerm().getType() - ) - | - intersectionType.implementsComparable() - ) - else uif.isOrEmbedsComparable() - ) - ) - } - - /** - * Holds if this type implements an interface that has the qualified name `pkg.name`, - * that is, the method set of `pkg.name` is contained in the method set of this type - * and any type restrictions are satisfied. - */ - predicate implements(string pkg, string name) { - exists(Type t | t.hasQualifiedName(pkg, name) | this.implements(t.getUnderlyingType())) - } - - /** - * Gets the pointer type that has this type as its base type. - */ - PointerType getPointerType() { result.getBaseType() = this } - - /** - * Gets a pretty-printed representation of this type, including its structure where applicable. - */ - string pp() { result = this.toString() } - - /** - * Gets a basic textual representation of this type. - */ - string toString() { result = this.getName() } -} - -/** An invalid type. */ -class InvalidType extends @invalidtype, Type { - override string toString() { result = "invalid type" } -} - -/** A basic type. */ -class BasicType extends @basictype, Type { } - -/** Either the normal or literal boolean type */ -class BoolType extends @booltype, BasicType { } - -/** The `bool` type of a non-literal expression */ -class BoolExprType extends @boolexprtype, BoolType { - override string getName() { result = "bool" } -} - -/** A numeric type such as `int` or `float64`. */ -class NumericType extends @numerictype, BasicType { - /** - * Gets the implementation-independent size (in bits) of this numeric type. - * - * This predicate is not defined for types with an implementation-specific size, that is, - * `uint`, `int` or `uintptr`. - */ - int getSize() { none() } - - /** - * Gets a possible implementation-specific size (in bits) of this numeric type. - * - * This predicate is not defined for `uintptr` since the language specification says nothing - * about its size. - */ - int getASize() { result = this.getSize() } -} - -/** An integer type such as `int` or `uint64`. */ -class IntegerType extends @integertype, NumericType { } - -/** A signed integer type such as `int`. */ -class SignedIntegerType extends @signedintegertype, IntegerType { } - -/** The type `int`. */ -class IntType extends @inttype, SignedIntegerType { - override int getASize() { result = 32 or result = 64 } - - override string getName() { result = "int" } -} - -/** The type `int8`. */ -class Int8Type extends @int8type, SignedIntegerType { - override int getSize() { result = 8 } - - override string getName() { result = "int8" } -} - -/** The type `int16`. */ -class Int16Type extends @int16type, SignedIntegerType { - override int getSize() { result = 16 } - - override string getName() { result = "int16" } -} - -/** The type `int32`. */ -class Int32Type extends @int32type, SignedIntegerType { - override int getSize() { result = 32 } - - override string getName() { result = "int32" } -} - -/** The type `int64`. */ -class Int64Type extends @int64type, SignedIntegerType { - override int getSize() { result = 64 } - - override string getName() { result = "int64" } -} - -/** An unsigned integer type such as `uint`. */ -class UnsignedIntegerType extends @unsignedintegertype, IntegerType { } - -/** The type `uint`. */ -class UintType extends @uinttype, UnsignedIntegerType { - override int getASize() { result = 32 or result = 64 } - - override string getName() { result = "uint" } -} - -/** The type `uint8`. */ -class Uint8Type extends @uint8type, UnsignedIntegerType { - override int getSize() { result = 8 } - - override string getName() { result = "uint8" } -} - -/** The type `uint16`. */ -class Uint16Type extends @uint16type, UnsignedIntegerType { - override int getSize() { result = 16 } - - override string getName() { result = "uint16" } -} - -/** The type `uint32`. */ -class Uint32Type extends @uint32type, UnsignedIntegerType { - override int getSize() { result = 32 } - - override string getName() { result = "uint32" } -} - -/** The type `uint64`. */ -class Uint64Type extends @uint64type, UnsignedIntegerType { - override int getSize() { result = 64 } - - override string getName() { result = "uint64" } -} - -/** The type `uintptr`. */ -class UintptrType extends @uintptrtype, BasicType { - override string getName() { result = "uintptr" } -} - -/** A floating-point type such as `float64`. */ -class FloatType extends @floattype, NumericType { } - -/** The type `float32`. */ -class Float32Type extends @float32type, FloatType { - override int getSize() { result = 32 } - - override string getName() { result = "float32" } -} - -/** The type `float64`. */ -class Float64Type extends @float64type, FloatType { - override int getSize() { result = 64 } - - override string getName() { result = "float64" } -} - -/** A complex-number type such as `complex64`. */ -class ComplexType extends @complextype, NumericType { } - -/** The type `complex64`. */ -class Complex64Type extends @complex64type, ComplexType { - override int getSize() { result = 64 } - - override string getName() { result = "complex64" } -} - -/** The type `complex128`. */ -class Complex128Type extends @complex128type, ComplexType { - override int getSize() { result = 128 } - - override string getName() { result = "complex128" } -} - -/** Either the normal or literal string type */ -class StringType extends @stringtype, BasicType { } - -/** The `string` type of a non-literal expression */ -class StringExprType extends @stringexprtype, StringType { - override string getName() { result = "string" } -} - -/** The type `unsafe.Pointer`. */ -class UnsafePointerType extends @unsafepointertype, BasicType { - override string getName() { result = "unsafe.Pointer" } -} - -/** The type of a literal. */ -class LiteralType extends @literaltype, BasicType { } - -/** The type of a bool literal. */ -class BoolLiteralType extends @boolliteraltype, LiteralType, BoolType { - override string toString() { result = "bool literal" } -} - -/** The type of an integer literal. */ -class IntLiteralType extends @intliteraltype, LiteralType, SignedIntegerType { - override string toString() { result = "int literal" } -} - -/** The type of a rune literal. */ -class RuneLiteralType extends @runeliteraltype, LiteralType, SignedIntegerType { - override string toString() { result = "rune literal" } -} - -/** The type of a float literal. */ -class FloatLiteralType extends @floatliteraltype, LiteralType, FloatType { - override string toString() { result = "float literal" } -} - -/** The type of a complex literal. */ -class ComplexLiteralType extends @complexliteraltype, LiteralType, ComplexType { - override string toString() { result = "complex literal" } -} - -/** The type of a string literal. */ -class StringLiteralType extends @stringliteraltype, LiteralType, StringType { - override string toString() { result = "string literal" } -} - -/** The type of `nil`. */ -class NilLiteralType extends @nilliteraltype, LiteralType { - override string toString() { result = "nil literal" } -} - -/** A composite type, that is, not a basic type. */ -class CompositeType extends @compositetype, Type { } - -/** A type that comes from a type parameter. */ -class TypeParamType extends @typeparamtype, CompositeType { - /** Gets the name of this type parameter type. */ - string getParamName() { typeparam(this, result, _, _, _) } - - /** Gets the constraint of this type parameter type. */ - Type getConstraint() { typeparam(this, _, result, _, _) } - - override InterfaceType getUnderlyingType() { result = this.getConstraint().getUnderlyingType() } - - override string pp() { result = this.getParamName() } - - /** - * Gets a pretty-printed representation of this type including its constraint. - */ - string ppWithConstraint() { result = this.getParamName() + " " + this.getConstraint().pp() } - - override string toString() { result = "type parameter type" } -} - -/** An array type. */ -class ArrayType extends @arraytype, CompositeType { - /** Gets the element type of this array type. */ - Type getElementType() { element_type(this, result) } - - /** Gets the length of this array type as a string. */ - string getLengthString() { array_length(this, result) } - - /** Gets the length of this array type if it can be represented as a QL integer. */ - int getLength() { result = this.getLengthString().toInt() } - - override Package getPackage() { result = this.getElementType().getPackage() } - - override string pp() { result = "[" + this.getLength() + "]" + this.getElementType().pp() } - - override string toString() { result = "array type" } -} - -/** A slice type. */ -class SliceType extends @slicetype, CompositeType { - /** Gets the element type of this slice type. */ - Type getElementType() { element_type(this, result) } - - override Package getPackage() { result = this.getElementType().getPackage() } - - override string pp() { result = "[]" + this.getElementType().pp() } - - override string toString() { result = "slice type" } -} - -/** A byte slice type */ -class ByteSliceType extends SliceType { - ByteSliceType() { this.getElementType() instanceof Uint8Type } -} - -/** A struct type. */ -class StructType extends @structtype, CompositeType { - /** - * Holds if this struct contains a field `name` with type `tp`; - * `isEmbedded` is true if the field is embedded. - * - * Note that this predicate does not take promoted fields into account. - */ - predicate hasOwnField(int i, string name, Type tp, boolean isEmbedded) { - exists(string n | component_types(this, i, n, tp) | - if n = "" - then ( - isEmbedded = true and - ( - name = tp.(NamedType).getName() - or - name = tp.(PointerType).getBaseType().(NamedType).getName() - ) - ) else ( - isEmbedded = false and - name = n - ) - ) - } - - /** - * Get a field with the name `name`; `isEmbedded` is true if the field is embedded. - * - * Note that this does not take promoted fields into account. - */ - Field getOwnField(string name, boolean isEmbedded) { - result.getDeclaringType() = this and - result.getName() = name and - this.hasOwnField(_, name, _, isEmbedded) - } - - /** - * Holds if there is an embedded field at `depth`, with either type `tp` or a pointer to `tp`. - */ - private predicate hasEmbeddedField(Type tp, int depth) { - exists(Field f | this.hasFieldCand(_, f, depth, true) | - tp = f.getType() or - tp = f.getType().(PointerType).getBaseType() - ) - } - - /** - * Gets a field of `embeddedParent`, which is then embedded into this struct type. - */ - Field getFieldOfEmbedded(Field embeddedParent, string name, int depth, boolean isEmbedded) { - // embeddedParent is a field of 'this' at depth 'depth - 1' - this.hasFieldCand(_, embeddedParent, depth - 1, true) and - // embeddedParent's type has the result field - exists(StructType embeddedType, Type fieldType | - fieldType = embeddedParent.getType().getUnderlyingType() and - pragma[only_bind_into](embeddedType) = - [fieldType, fieldType.(PointerType).getBaseType().getUnderlyingType()] - | - result = embeddedType.getOwnField(name, isEmbedded) - ) - } - - /** - * Gets a method of `embeddedParent`, which is then embedded into this struct type. - */ - Method getMethodOfEmbedded(Field embeddedParent, string name, int depth) { - // embeddedParent is a field of 'this' at depth 'depth - 1' - this.hasFieldCand(_, embeddedParent, depth - 1, true) and - result.getName() = name and - ( - result.getReceiverBaseType() = embeddedParent.getType() - or - result.getReceiverBaseType() = embeddedParent.getType().(PointerType).getBaseType() - or - methodhosts(result, embeddedParent.getType()) - ) - } - - private predicate hasFieldCand(string name, Field f, int depth, boolean isEmbedded) { - f = this.getOwnField(name, isEmbedded) and depth = 0 - or - not this.hasOwnField(_, name, _, _) and - f = this.getFieldOfEmbedded(_, name, depth, isEmbedded) - } - - private predicate hasMethodCand(string name, Method m, int depth) { - name = m.getName() and - exists(Type embedded | this.hasEmbeddedField(embedded, depth - 1) | - m.getReceiverType() = embedded - ) - } - - /** - * Holds if this struct contains a field `name` with type `tp`, possibly inside a (nested) - * embedded field. - */ - predicate hasField(string name, Type tp) { - exists(int mindepth | - mindepth = min(int depth | this.hasFieldCand(name, _, depth, _)) and - tp = unique(Field f | f = this.getFieldCand(name, mindepth, _)).getType() - ) - } - - private Field getFieldCand(string name, int depth, boolean isEmbedded) { - result = this.getOwnField(name, isEmbedded) and depth = 0 - or - exists(Type embedded | this.hasEmbeddedField(embedded, depth - 1) | - result = embedded.getUnderlyingType().(StructType).getOwnField(name, isEmbedded) - ) - } - - override Field getField(string name) { result = this.getFieldAtDepth(name, _) } - - /** - * Gets the field `f` with depth `depth` of this type. - * - * This includes fields promoted from an embedded field. It is not possible - * to access a field that is shadowed by a promoted field with this function. - * The number of embedded fields traversed to reach `f` is called its depth. - * The depth of a field `f` declared in this type is zero. - */ - Field getFieldAtDepth(string name, int depth) { - depth = min(int depthCand | exists(this.getFieldCand(name, depthCand, _))) and - result = this.getFieldCand(name, depth, _) and - strictcount(this.getFieldCand(name, depth, _)) = 1 - } - - Method getMethodAtDepth(string name, int depth) { - depth = min(int depthCand | this.hasMethodCand(name, _, depthCand)) and - result = unique(Method m | this.hasMethodCand(name, m, depth)) - } - - override predicate hasMethod(string name, SignatureType tp) { - exists(int mindepth | - mindepth = min(int depth | this.hasMethodCand(name, _, depth)) and - tp = unique(Method m | this.hasMethodCand(name, m, mindepth)).getType() - ) - } - - language[monotonicAggregates] - override string pp() { - result = - "struct { " + - concat(int i, string name, Type tp | - component_types(this, i, name, tp) - | - name + " " + tp.pp(), "; " order by i - ) + " }" - } - - override string toString() { result = "struct type" } -} - -/** A pointer type. */ -class PointerType extends @pointertype, CompositeType { - /** Gets the base type of this pointer type. */ - Type getBaseType() { base_type(this, result) } - - override Package getPackage() { result = this.getBaseType().getPackage() } - - override Method getMethod(string m) { - result = CompositeType.super.getMethod(m) - or - // https://golang.org/ref/spec#Method_sets: "the method set of a pointer type *T is - // the set of all methods declared with receiver *T or T" - result = this.getBaseType().getMethod(m) - or - // promoted methods from embedded types - exists(StructType s, Type embedded | - s = this.getBaseType().(NamedType).getUnderlyingType() and - s.hasOwnField(_, _, embedded, true) and - // ensure that `m` can be promoted - not s.hasOwnField(_, m, _, _) and - not exists(Method m2 | m2.getReceiverBaseType() = this.getBaseType() and m2.getName() = m) - | - result = embedded.getMethod(m) - or - // If S contains an embedded field T, the method set of *S includes promoted methods with receiver T or T* - not embedded instanceof PointerType and - result = embedded.getPointerType().getMethod(m) - or - // If S contains an embedded field *T, the method set of *S includes promoted methods with receiver T or *T - result = embedded.(PointerType).getBaseType().getMethod(m) - ) - } - - override string pp() { result = "* " + this.getBaseType().pp() } - - override string toString() { result = "pointer type" } -} - -private newtype TTypeSetTerm = - MkTypeSetTerm(TypeSetLiteralType tslit, int index) { component_types(tslit, index, _, _) } - -/** - * A term in a type set literal. - * - * Examples: - * ```go - * int - * ~string - * ``` - */ -class TypeSetTerm extends TTypeSetTerm { - boolean tilde; - Type tp; - - TypeSetTerm() { - exists(TypeSetLiteralType tslit, int index | - this = MkTypeSetTerm(tslit, index) and - ( - component_types(tslit, index, "", tp) and - tilde = false - or - component_types(tslit, index, "~", tp) and - tilde = true - ) - ) - } - - /** - * Holds if this term has a tilde in front of it. - * - * A tilde is used to indicate that the term refers to all types with a given - * underlying type. - */ - predicate hasTilde() { tilde = true } - - /** Gets the type of this term. */ - Type getType() { result = tp } - - /** Holds if `t` is in the type set of this term. */ - predicate includesType(Type t) { if tilde = false then t = tp else t.getUnderlyingType() = tp } - - /** Gets a pretty-printed representation of this term. */ - string pp() { - exists(string tildeStr | if tilde = true then tildeStr = "~" else tildeStr = "" | - result = tildeStr + tp.pp() - ) - } - - /** Gets a textual representation of this element. */ - string toString() { result = "type set term" } -} - -private TypeSetTerm getIntersection(TypeSetTerm term1, TypeSetTerm term2) { - term1.getType() = term2.getType() and - if term1.hasTilde() then result = term2 else result = term1 -} - -/** - * Gets a term in the intersection of type-set literals `a` and `b`. - */ -TypeSetTerm getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { - result = getIntersection(a.getATerm(), b.getATerm()) -} - -/** - * A type set literal type, used when declaring a non-basic interface. May be a - * single term, consisting of either a type or a tilde followed by a type, or a - * union of terms. - * - * - * Examples: - * - * ```go - * int - * ~string - * int | ~string - * ``` - */ -class TypeSetLiteralType extends @typesetliteraltype, CompositeType { - /** Gets the `i`th term in this type set literal. */ - TypeSetTerm getTerm(int i) { result = MkTypeSetTerm(this, i) } - - /** Gets a term in this type set literal. */ - TypeSetTerm getATerm() { result = this.getTerm(_) } - - /** Holds if `t` is in the type set of this type set literal. */ - predicate includesType(Type t) { this.getATerm().includesType(t) } - - /** - * Gets the interface type of which this type-set literal is the only - * element, if it exists. - * - * It exists if it has been explicitly defined, as in - * `interface { int64 | uint64 }`, or if it has been implicitly created by - * using the type set literal directly as the bound in a type parameter - * declaration, as in `[T int64 | uint64]`. - */ - InterfaceType getInterfaceType() { - this = result.getDirectlyEmbeddedTypeSetLiteral(0) and - not exists(result.getDirectlyEmbeddedTypeSetLiteral(1)) and - hasNoMethods(result) and - not exists(result.getADirectlyEmbeddedInterface()) - } - - language[monotonicAggregates] - override string pp() { - result = concat(TypeSetTerm t, int i | t = this.getTerm(i) | t.pp(), " | " order by i) - } - - override string toString() { result = "type set literal type" } -} - -/** An interface type. */ -class InterfaceType extends @interfacetype, CompositeType { - /** Gets the type of method `name` of this interface type. */ - Type getMethodType(string name) { - // Note that negative indices correspond to embedded interfaces and type - // set literals. - exists(int i | i >= 0 | component_types(this, i, name, result)) - } - - override predicate hasMethod(string m, SignatureType t) { t = this.getMethodType(m) } - - /** - * Holds if `tp` is a directly embedded type with index `index`. - * - * `tp` (or its underlying type) is either a type set literal type or an - * interface type. - */ - private predicate hasDirectlyEmbeddedType(int index, Type tp) { - index >= 0 and component_types(this, -(index + 1), _, tp) - } - - /** - * Gets a type whose underlying type is an interface that is directly - * embedded into this interface. - * - * Note that the methods of the embedded interface are already considered - * as part of the method set of this interface. - */ - Type getADirectlyEmbeddedInterface() { - this.hasDirectlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType - } - - /** - * Gets a type whose underlying type is an interface that is embedded into - * this interface. - * - * Note that the methods of the embedded interface are already considered - * as part of the method set of this interface. - */ - Type getAnEmbeddedInterface() { - result = this.getADirectlyEmbeddedInterface() or - result = - this.getADirectlyEmbeddedInterface() - .getUnderlyingType() - .(InterfaceType) - .getAnEmbeddedInterface() - } - - /** - * Holds if this interface type is (the underlying type of) `comparable`, or - * it embeds `comparable`. - */ - predicate isOrEmbedsComparable() { - this.getAnEmbeddedInterface() instanceof ComparableType or - this = any(ComparableType comparable).getUnderlyingType() - } - - /** - * Gets the type set literal with index `index` from the definition of this - * interface type. - * - * Note that the indexes are not contiguous. - */ - TypeSetLiteralType getDirectlyEmbeddedTypeSetLiteral(int index) { - hasDirectlyEmbeddedType(index, result) - } - - /** - * Gets a type set literal of this interface type. - * - * This includes type set literals of embedded interfaces. - */ - TypeSetLiteralType getAnEmbeddedTypeSetLiteral() { - result = this.getDirectlyEmbeddedTypeSetLiteral(_) or - result = - getADirectlyEmbeddedInterface() - .getUnderlyingType() - .(InterfaceType) - .getAnEmbeddedTypeSetLiteral() - } - - language[monotonicAggregates] - override string pp() { - exists(string comp, string sep1, string ts, string sep2, string meth | - // Note that the interface type underlying `comparable` will be printed - // as `interface { comparable }`, which is not entirely accurate, but - // also better than anything else I can think of. - (if this.isOrEmbedsComparable() then comp = " comparable" else comp = "") and - ts = - concat(TypeSetLiteralType tslit | - tslit = this.getAnEmbeddedTypeSetLiteral() - | - " " + tslit.pp(), ";" - ) and - meth = - concat(string name, Type tp | - tp = this.getMethodType(name) - | - " " + name + " " + tp.pp(), ";" order by name - ) and - (if comp != "" and ts != "" then sep1 = ";" else sep1 = "") and - if - (comp != "" or ts != "") and - meth != "" - then sep2 = ";" - else sep2 = "" - | - result = "interface {" + comp + sep1 + ts + sep2 + meth + " }" - ) - } - - override string toString() { result = "interface type" } -} - -// This predicate is needed for performance reasons. -pragma[noinline] -private predicate hasNoMethods(InterfaceType i) { not i.hasMethod(_, _) } - -/** - * A basic interface type. - * - * A basic interface is an interface that does not specify any type set - * literals, and which does not embed any non-basic interfaces. The special - * interface `comparable` is not a basic interface. - */ -class BasicInterfaceType extends InterfaceType { - BasicInterfaceType() { - not exists(this.getAnEmbeddedTypeSetLiteral()) and - not this.isOrEmbedsComparable() - } - - override string toString() { result = "basic interface type" } -} - -/** - * An empty interface type. - * - * Note that by we have to be careful to exclude the underlying type of - * `comparable`. This is done by extending `BasicInterfaceType`. - */ -class EmptyInterfaceType extends BasicInterfaceType { - EmptyInterfaceType() { hasNoMethods(this) } -} - -/** - * The predeclared `comparable` type. - */ -class ComparableType extends NamedType { - ComparableType() { this.getName() = "comparable" } -} - -/** A tuple type. */ -class TupleType extends @tupletype, CompositeType { - /** Gets the `i`th component type of this tuple type. */ - Type getComponentType(int i) { component_types(this, i, _, result) } - - language[monotonicAggregates] - override string pp() { - result = - "(" + concat(int i, Type tp | tp = this.getComponentType(i) | tp.pp(), ", " order by i) + ")" - } - - override string toString() { result = "tuple type" } -} - -/** A signature type. */ -class SignatureType extends @signaturetype, CompositeType { - /** Gets the `i`th parameter type of this signature type. */ - Type getParameterType(int i) { i >= 0 and component_types(this, i + 1, _, result) } - - /** Gets the `i`th result type of this signature type. */ - Type getResultType(int i) { i >= 0 and component_types(this, -(i + 1), _, result) } - - /** Gets the number of parameters specified by this signature. */ - int getNumParameter() { result = count(int i | exists(this.getParameterType(i))) } - - /** Gets the number of results specified by this signature. */ - int getNumResult() { result = count(int i | exists(this.getResultType(i))) } - - /** Holds if this signature type is variadic. */ - predicate isVariadic() { variadic(this) } - - language[monotonicAggregates] - override string pp() { - result = - "func(" + concat(int i, Type tp | tp = this.getParameterType(i) | tp.pp(), ", " order by i) + - ") " + concat(int i, Type tp | tp = this.getResultType(i) | tp.pp(), ", " order by i) - } - - override string toString() { result = "signature type" } -} - -/** A map type. */ -class MapType extends @maptype, CompositeType { - /** Gets the key type of this map type. */ - Type getKeyType() { key_type(this, result) } - - /** Gets the value type of this map type. */ - Type getValueType() { element_type(this, result) } - - override string pp() { result = "[" + this.getKeyType().pp() + "]" + this.getValueType().pp() } - - override string toString() { result = "map type" } -} - -/** A channel type. */ -class ChanType extends @chantype, CompositeType { - /** Gets the element type of this channel type. */ - Type getElementType() { element_type(this, result) } - - /** Holds if this channel can send data. */ - predicate canSend() { none() } - - /** Holds if this channel can receive data. */ - predicate canReceive() { none() } -} - -/** A channel type that can only send. */ -class SendChanType extends @sendchantype, ChanType { - override predicate canSend() { any() } - - override string pp() { result = "chan<- " + this.getElementType().pp() } - - override string toString() { result = "send-channel type" } -} - -/** A channel type that can only receive. */ -class RecvChanType extends @recvchantype, ChanType { - override predicate canReceive() { any() } - - override string pp() { result = "<-chan " + this.getElementType().pp() } - - override string toString() { result = "receive-channel type" } -} - -/** A channel type that can both send and receive. */ -class SendRecvChanType extends @sendrcvchantype, ChanType { - override predicate canSend() { any() } - - override predicate canReceive() { any() } - - override string pp() { result = "chan " + this.getElementType().pp() } - - override string toString() { result = "send-receive-channel type" } -} - -/** A named type. */ -class NamedType extends @namedtype, CompositeType { - /** Gets the type which this type is defined to be. */ - Type getBaseType() { underlying_type(this, result) } - - override Method getMethod(string m) { - result = CompositeType.super.getMethod(m) - or - methodhosts(result, this) and - result.getName() = m - or - // handle promoted methods - exists(StructType s, Type embedded | - s = this.getBaseType() and - s.hasOwnField(_, _, embedded, true) and - // ensure `m` can be promoted - not s.hasOwnField(_, m, _, _) and - not exists(Method m2 | m2.getReceiverType() = this and m2.getName() = m) - | - // If S contains an embedded field T, the method set of S includes promoted methods with receiver T - result = embedded.getMethod(m) - or - // If S contains an embedded field *T, the method set of S includes promoted methods with receiver T or *T - result = embedded.(PointerType).getBaseType().getMethod(m) - ) - } - - override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() } -} - -/** - * A type that implements the builtin interface `error`. - */ -class ErrorType extends Type { - ErrorType() { this.implements(Builtin::error().getType().getUnderlyingType()) } -} - -/** - * Gets the name of a method in the method set of `i`. - * - * This is used to restrict the set of interfaces to consider in the definition of `implements`, - * so it does not matter which method name is chosen (we use the lexicographically least). - */ -private string getExampleMethodName(InterfaceType i) { result = min(string m | i.hasMethod(m, _)) } diff --git a/ql/lib/semmle/go/Util.qll b/ql/lib/semmle/go/Util.qll deleted file mode 100644 index ff597cdcc..000000000 --- a/ql/lib/semmle/go/Util.qll +++ /dev/null @@ -1,18 +0,0 @@ -/** This module provides general utility classes and predicates. */ - -/** - * A Boolean value. - * - * This is a self-binding convenience wrapper for `boolean`. - */ -class Boolean extends boolean { - Boolean() { this = true or this = false } -} - -/** - * Gets a regexp pattern that matches common top-level domain names. - */ -string commonTLD() { - // according to ranking by http://google.com/search?q=site:.<> - result = "(?:com|org|edu|gov|uk|net|io)(?![a-z0-9])" -} diff --git a/ql/lib/semmle/go/VariableWithFields.qll b/ql/lib/semmle/go/VariableWithFields.qll deleted file mode 100644 index adb5e2b30..000000000 --- a/ql/lib/semmle/go/VariableWithFields.qll +++ /dev/null @@ -1,198 +0,0 @@ -/** Provides the `VariableWithFields` class, for working with variables with a chain of field or element accesses chained to it. */ - -import go - -private newtype TVariableWithFields = - TVariableRoot(Variable v) or - TVariableFieldStep(VariableWithFields base, Field f) { - exists(fieldAccessPathAux(base, f)) or exists(fieldWriteAccessPathAux(base, f)) - } or - TVariableElementStep(VariableWithFields base, string e) { - exists(elementAccessPathAux(base, e)) or exists(elementWriteAccessPathAux(base, e)) - } - -/** - * Gets a representation of the write target `wt` as a variable with fields value if there is one. - */ -private TVariableWithFields writeAccessPath(IR::WriteTarget wt) { - exists(Variable v | wt = v.getAWrite().getLhs() | result = TVariableRoot(v)) - or - exists(VariableWithFields base, Field f | wt = fieldWriteAccessPathAux(base, f) | - result = TVariableFieldStep(base, f) - ) - or - exists(VariableWithFields base, string e | wt = elementWriteAccessPathAux(base, e) | - result = TVariableElementStep(base, e) - ) -} - -/** - * Gets a representation of `insn` as a variable with fields value if there is one. - */ -private TVariableWithFields accessPath(IR::Instruction insn) { - exists(Variable v | insn = v.getARead().asInstruction() | result = TVariableRoot(v)) - or - exists(VariableWithFields base, Field f | insn = fieldAccessPathAux(base, f) | - result = TVariableFieldStep(base, f) - ) - or - exists(VariableWithFields base, string e | insn = elementAccessPathAux(base, e) | - result = TVariableElementStep(base, e) - ) -} - -/** - * Gets an IR instruction that reads a field `f` from a node that is represented - * by variable with fields value `base`. - */ -private IR::Instruction fieldAccessPathAux(TVariableWithFields base, Field f) { - exists(IR::FieldReadInstruction fr, IR::Instruction frb | - fr.getBase() = frb or - fr.getBase() = IR::implicitDerefInstruction(frb.(IR::EvalInstruction).getExpr()) - | - base = accessPath(frb) and - f = fr.getField() and - result = fr - ) -} - -/** - * Gets an IR write target that represents a field `f` from a node that is represented - * by variable with fields value `base`. - */ -private IR::WriteTarget fieldWriteAccessPathAux(TVariableWithFields base, Field f) { - exists(IR::FieldTarget ft, IR::Instruction ftb | - ft.getBase() = ftb or - ft.getBase() = IR::implicitDerefInstruction(ftb.(IR::EvalInstruction).getExpr()) - | - base = accessPath(ftb) and - ft.getField() = f and - result = ft - ) -} - -/** - * Gets an IR instruction that reads an element `e` from a node that is represented - * by variable with fields value `base`. - */ -private IR::Instruction elementAccessPathAux(TVariableWithFields base, string e) { - exists(IR::ElementReadInstruction er, IR::EvalInstruction erb | - er.getBase() = erb or - er.getBase() = IR::implicitDerefInstruction(erb.getExpr()) - | - base = accessPath(erb) and - e = er.getIndex().getExactValue() and - result = er - ) -} - -/** - * Gets an IR write target that represents an element `e` from a node that is represented - * by variable with fields value `base`. - */ -private IR::WriteTarget elementWriteAccessPathAux(TVariableWithFields base, string e) { - exists(IR::ElementTarget et, IR::EvalInstruction etb | - et.getBase() = etb or - et.getBase() = IR::implicitDerefInstruction(etb.getExpr()) - | - base = accessPath(etb) and - e = et.getIndex().getExactValue() and - result = et - ) -} - -/** A variable with zero or more fields or elements read from it. */ -class VariableWithFields extends TVariableWithFields { - /** - * Gets the variable corresponding to the base of this variable with fields. - * - * For example, the variable corresponding to `a` for the variable with fields - * corresponding to `a.b[c]`. - */ - Variable getBaseVariable() { this.getParent*() = TVariableRoot(result) } - - /** - * Gets the variable with fields corresponding to the parent of this variable with fields. - * - * For example, the variable with fields corresponding to `a.b` for the variable with fields - * corresponding to `a.b[c]`. - */ - VariableWithFields getParent() { - exists(VariableWithFields base | - this = TVariableFieldStep(base, _) or this = TVariableElementStep(base, _) - | - result = base - ) - } - - /** Gets a use that refers to this variable with fields. */ - DataFlow::Node getAUse() { this = accessPath(result.asInstruction()) } - - /** Gets the type of this variable with fields. */ - Type getType() { - exists(IR::Instruction acc | this = accessPath(acc) | result = acc.getResultType()) - } - - /** Gets a textual representation of this element. */ - string toString() { - exists(Variable var | this = TVariableRoot(var) | result = "(" + var + ")") - or - exists(VariableWithFields base, Field f | this = TVariableFieldStep(base, f) | - result = base + "." + f.getName() - ) - or - exists(VariableWithFields base, string e | this = TVariableElementStep(base, e) | - result = base + "[" + e + "]" - ) - } - - /** - * Gets the qualified name of the source variable or variable and fields that this represents. - * - * For example, for the variable with fields that represents the field `a.b[c]`, this would get the string - * `"a.b.c"`. - */ - string getQualifiedName() { - exists(Variable v | this = TVariableRoot(v) | result = v.getName()) - or - exists(VariableWithFields base, Field f | this = TVariableFieldStep(base, f) | - result = base.getQualifiedName() + "." + f.getName() - ) - or - exists(VariableWithFields base, string e | this = TVariableElementStep(base, e) | - result = base.getQualifiedName() + "." + e.replaceAll(".", "\\.") - ) - } - - /** - * Gets a write of this variable with fields. - */ - Write getAWrite() { this = writeAccessPath(result.getLhs()) } - - /** - * Gets the field that is the last step of this variable with fields, if any. - * - * For example, the field `c` for the variable with fields `a.b.c`. - */ - Field getField() { this = TVariableFieldStep(_, result) } - - /** - * Gets the element that this variable with fields reads, if any. - * - * For example, the string value of `c` for the variable with fields `a.b[c]`. - */ - string getElement() { this = TVariableElementStep(_, result) } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getBaseVariable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} diff --git a/ql/lib/semmle/go/concepts/GeneratedFile.qll b/ql/lib/semmle/go/concepts/GeneratedFile.qll deleted file mode 100644 index dec687212..000000000 --- a/ql/lib/semmle/go/concepts/GeneratedFile.qll +++ /dev/null @@ -1,50 +0,0 @@ -/** Provides a class for generated files. */ - -import go - -/** Provides a class for generated files. */ -module GeneratedFile { - /** - * A file that has been generated. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `GeneratedFile` instead. - */ - abstract class Range extends File { } - - private string generatorCommentRegex() { - result = - [ - "Generated By\\b.*\\bDo not edit", - "This (file|class|interface|art[ei]fact) (was|is|(has been)) (?:auto[ -]?)?gener(e?)ated", - "Any modifications to this file will be lost", - "This (file|class|interface|art[ei]fact) (was|is) (?:mechanically|automatically) generated", - "The following code was (?:auto[ -]?)?generated (?:by|from)", "Autogenerated by Thrift", - "(Code g|G)enerated from .* by ANTLR" - ] - } - - private class CommentHeuristicGeneratedFile extends Range { - CommentHeuristicGeneratedFile() { - exists(Comment c | c.getFile() = this | - c.getText().regexpMatch("(?i).*\\b(" + concat(generatorCommentRegex(), "|") + ")\\b.*") - or - // regular expression recommended for Go code generators - // (https://golang.org/pkg/cmd/go/internal/generate/) - c.getText().regexpMatch("^\\s*Code generated .* DO NOT EDIT\\.\\s*$") - ) - } - } -} - -/** - * A file that has been generated. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `GeneratedFile::Range` instead. - */ -class GeneratedFile extends File { - GeneratedFile::Range self; - - GeneratedFile() { this = self } -} diff --git a/ql/lib/semmle/go/concepts/HTTP.qll b/ql/lib/semmle/go/concepts/HTTP.qll deleted file mode 100644 index 89a716d4f..000000000 --- a/ql/lib/semmle/go/concepts/HTTP.qll +++ /dev/null @@ -1,380 +0,0 @@ -/** - * Provides classes for working with HTTP-related concepts such as requests and responses. - */ - -import go - -/** Provides classes for modeling HTTP-related APIs. */ -module HTTP { - /** Provides a class for modeling new HTTP response-writer APIs. */ - module ResponseWriter { - /** - * A variable that is an HTTP response writer. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::ResponseWriter` instead. - */ - abstract class Range extends Variable { - /** - * Gets a data-flow node that is a use of this response writer. - * - * Note that `PostUpdateNode`s for nodes that this predicate gets do not need to be - * included, as they are handled by the concrete `ResponseWriter`'s `getANode`. - */ - abstract DataFlow::Node getANode(); - } - } - - /** - * A variable that is an HTTP response writer. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::ResponseWriter::Range` instead. - */ - class ResponseWriter extends Variable { - ResponseWriter::Range self; - - ResponseWriter() { this = self } - - /** Gets the body that is written in this HTTP response. */ - ResponseBody getBody() { result.getResponseWriter() = this } - - /** Gets a header write that is written in this HTTP response. */ - HeaderWrite getAHeaderWrite() { result.getResponseWriter() = this } - - /** Gets a redirect that is sent in this HTTP response. */ - Redirect getARedirect() { result.getResponseWriter() = this } - - /** Gets a data-flow node that is a use of this response writer. */ - DataFlow::Node getANode() { - result = self.getANode() or - result.(DataFlow::PostUpdateNode).getPreUpdateNode() = self.getANode() - } - } - - /** Provides a class for modeling new HTTP header-write APIs. */ - module HeaderWrite { - /** - * A data-flow node that represents a write to an HTTP header. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::HeaderWrite` instead. - */ - abstract class Range extends DataFlow::ExprNode { - /** Gets the (lower-case) name of a header set by this definition. */ - string getHeaderName() { result = this.getName().getStringValue().toLowerCase() } - - /** Gets the value of the header set by this definition. */ - string getHeaderValue() { - result = this.getValue().getStringValue() - or - result = this.getValue().getIntValue().toString() - } - - /** Holds if this header write defines the header `header`. */ - predicate definesHeader(string header, string value) { - header = this.getHeaderName() and - value = this.getHeaderValue() - } - - /** - * Gets the node representing the name of the header defined by this write. - * - * Note that a `HeaderWrite` targeting a constant header (e.g. a routine that always - * sets the `Content-Type` header) may not have such a node, so callers should use - * `getHeaderName` in preference to this method). - */ - abstract DataFlow::Node getName(); - - /** Gets the node representing the value of the header defined by this write. */ - abstract DataFlow::Node getValue(); - - /** Gets the response writer associated with this header write, if any. */ - abstract ResponseWriter getResponseWriter(); - } - } - - /** - * A data-flow node that represents a write to an HTTP header. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::HeaderWrite::Range` instead. - */ - class HeaderWrite extends DataFlow::ExprNode { - HeaderWrite::Range self; - - HeaderWrite() { this = self } - - /** Gets the (lower-case) name of a header set by this definition. */ - string getHeaderName() { result = self.getHeaderName() } - - /** Gets the value of the header set by this definition. */ - string getHeaderValue() { result = self.getHeaderValue() } - - /** Holds if this header write defines the header `header`. */ - predicate definesHeader(string header, string value) { self.definesHeader(header, value) } - - /** - * Gets the node representing the name of the header defined by this write. - * - * Note that a `HeaderWrite` targeting a constant header (e.g. a routine that always - * sets the `Content-Type` header) may not have such a node, so callers should use - * `getHeaderName` in preference to this method). - */ - DataFlow::Node getName() { result = self.getName() } - - /** Gets the node representing the value of the header defined by this write. */ - DataFlow::Node getValue() { result = self.getValue() } - - /** Gets the response writer associated with this header write, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } - } - - /** A data-flow node whose value is written to an HTTP header. */ - class Header extends DataFlow::Node { - HeaderWrite hw; - - Header() { - this = hw.getName() - or - this = hw.getValue() - } - - /** Gets the response writer associated with this header write, if any. */ - ResponseWriter getResponseWriter() { result = hw.getResponseWriter() } - } - - /** A data-flow node whose value is written to the value of an HTTP header. */ - class HeaderValue extends Header { - HeaderValue() { this = hw.getValue() } - } - - /** A data-flow node whose value is written to the name of an HTTP header. */ - class HeaderName extends Header { - HeaderName() { this = hw.getName() } - } - - /** Provides a class for modeling new HTTP request-body APIs. */ - module RequestBody { - /** - * An expression representing a reader whose content is written to an HTTP request body. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::RequestBody` instead. - */ - abstract class Range extends DataFlow::Node { } - } - - /** - * An expression representing a reader whose content is written to an HTTP request body. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::RequestBody::Range` instead. - */ - class RequestBody extends DataFlow::Node { - RequestBody::Range self; - - RequestBody() { this = self } - } - - /** Provides a class for modeling new HTTP response-body APIs. */ - module ResponseBody { - /** - * An expression which is written to an HTTP response body. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::ResponseBody` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets the response writer associated with this header write, if any. */ - abstract ResponseWriter getResponseWriter(); - - /** Gets a content-type associated with this body. */ - string getAContentType() { - exists(HTTP::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | - hw.getHeaderName() = "content-type" and - result = hw.getHeaderValue() - ) - or - result = this.getAContentTypeNode().getStringValue() - } - - /** Gets a dataflow node for a content-type associated with this body. */ - DataFlow::Node getAContentTypeNode() { - exists(HTTP::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | - hw.getHeaderName() = "content-type" and - result = hw.getValue() - ) - } - } - } - - /** - * An expression which is written to an HTTP response body. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::ResponseBody::Range` instead. - */ - class ResponseBody extends DataFlow::Node { - ResponseBody::Range self; - - ResponseBody() { this = self } - - /** Gets the response writer associated with this header write, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } - - /** Gets a content-type associated with this body. */ - string getAContentType() { result = self.getAContentType() } - - /** Gets a dataflow node for a content-type associated with this body. */ - DataFlow::Node getAContentTypeNode() { result = self.getAContentTypeNode() } - } - - /** Provides a class for modeling new HTTP template response-body APIs. */ - module TemplateResponseBody { - /** - * An expression which is written to an HTTP response body via a template execution. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::ResponseBody` instead. - */ - abstract class Range extends ResponseBody::Range { - /** Gets the read of the variable inside the template where this value is read. */ - abstract HtmlTemplate::TemplateRead getRead(); - } - } - - /** - * An expression which is written to an HTTP response body via a template execution. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::TemplateResponseBody::Range` instead. - */ - class TemplateResponseBody extends ResponseBody { - override TemplateResponseBody::Range self; - - TemplateResponseBody() { this = self } - - /** Gets the read of the variable inside the template where this value is read. */ - HtmlTemplate::TemplateRead getRead() { result = self.getRead() } - } - - /** Provides a class for modeling new HTTP client request APIs. */ - module ClientRequest { - /** - * A call that performs a request to a URL. - * - * Example: An HTTP POST request is a client request that sends some - * `data` to a `url`, where both the headers and the body of the request - * contribute to the `data`. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::ClientRequest` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the URL of the request. - */ - abstract DataFlow::Node getUrl(); - } - } - - /** - * A call that performs a request to a URL. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::ClientRequest::Range` instead. - */ - class ClientRequest extends DataFlow::Node { - ClientRequest::Range self; - - ClientRequest() { this = self } - - /** - * Gets the URL of the request. - */ - DataFlow::Node getUrl() { result = self.getUrl() } - } - - /** Provides a class for modeling new HTTP redirect APIs. */ - module Redirect { - /** - * An HTTP redirect. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::Redirect` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets the data-flow node representing the URL being redirected to. */ - abstract DataFlow::Node getUrl(); - - /** Gets the response writer that this redirect is sent on, if any. */ - abstract ResponseWriter getResponseWriter(); - } - - /** - * An assignment of the HTTP Location header, which indicates the location for a - * redirect. - */ - private class LocationHeaderSet extends Range, HeaderWrite { - LocationHeaderSet() { this.getHeaderName() = "location" } - - override DataFlow::Node getUrl() { result = this.getValue() } - - override ResponseWriter getResponseWriter() { result = HeaderWrite.super.getResponseWriter() } - } - - /** - * An HTTP request attribute that is generally not attacker-controllable for - * open redirect exploits; for example, a form field submitted in a POST request. - */ - abstract class UnexploitableSource extends DataFlow::Node { } - } - - /** - * An HTTP redirect. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::Redirect::Range` instead. - */ - class Redirect extends DataFlow::Node { - Redirect::Range self; - - Redirect() { this = self } - - /** Gets the data-flow node representing the URL being redirected to. */ - DataFlow::Node getUrl() { result = self.getUrl() } - - /** Gets the response writer that this redirect is sent on, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } - } - - /** Provides a class for modeling new HTTP handler APIs. */ - module RequestHandler { - /** - * An HTTP request handler. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `HTTP::RequestHandler` instead. - */ - abstract class Range extends DataFlow::Node { - /** Gets a node that is used in a check that is tested before this handler is run. */ - abstract predicate guardedBy(DataFlow::Node check); - } - } - - /** - * An HTTP request handler. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `HTTP::RequestHandler::Range` instead. - */ - class RequestHandler extends DataFlow::Node { - RequestHandler::Range self; - - RequestHandler() { this = self } - - /** Gets a node that is used in a check that is tested before this handler is run. */ - predicate guardedBy(DataFlow::Node check) { self.guardedBy(check) } - } -} diff --git a/ql/lib/semmle/go/controlflow/BasicBlocks.qll b/ql/lib/semmle/go/controlflow/BasicBlocks.qll deleted file mode 100644 index 39b7590d8..000000000 --- a/ql/lib/semmle/go/controlflow/BasicBlocks.qll +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Provides classes for working with basic blocks. - */ - -import go -private import ControlFlowGraphImpl - -/** - * Holds if `nd` starts a new basic block. - */ -private predicate startsBB(ControlFlow::Node nd) { - count(nd.getAPredecessor()) != 1 - or - nd.getAPredecessor().isBranch() -} - -/** - * Holds if the first node of basic block `succ` is a control flow - * successor of the last node of basic block `bb`. - */ -private predicate succBB(BasicBlock bb, BasicBlock succ) { succ = bb.getLastNode().getASuccessor() } - -/** - * Holds if the first node of basic block `bb` is a control flow - * successor of the last node of basic block `pre`. - */ -private predicate predBB(BasicBlock bb, BasicBlock pre) { succBB(pre, bb) } - -/** Holds if `bb` is an entry basic block. */ -private predicate entryBB(BasicBlock bb) { bb.getFirstNode().isEntryNode() } - -/** Holds if `bb` is an exit basic block. */ -private predicate exitBB(BasicBlock bb) { bb.getLastNode().isExitNode() } - -cached -private module Internal { - /** - * Holds if `succ` is a control flow successor of `nd` within the same basic block. - */ - private predicate intraBBSucc(ControlFlow::Node nd, ControlFlow::Node succ) { - succ = nd.getASuccessor() and - not startsBB(succ) - } - - /** - * Holds if `nd` is the `i`th node in basic block `bb`. - * - * In other words, `i` is the shortest distance from a node `bb` - * that starts a basic block to `nd` along the `intraBBSucc` relation. - */ - cached - predicate bbIndex(BasicBlock bb, ControlFlow::Node nd, int i) = - shortestDistances(startsBB/1, intraBBSucc/2)(bb, nd, i) - - cached - int bbLength(BasicBlock bb) { result = strictcount(ControlFlow::Node nd | bbIndex(bb, nd, _)) } - - cached - predicate reachableBB(BasicBlock bb) { - entryBB(bb) - or - exists(BasicBlock predBB | succBB(predBB, bb) | reachableBB(predBB)) - } -} - -private import Internal - -/** Holds if `dom` is an immediate dominator of `bb`. */ -cached -private predicate bbIDominates(BasicBlock dom, BasicBlock bb) = - idominance(entryBB/1, succBB/2)(_, dom, bb) - -/** Holds if `dom` is an immediate post-dominator of `bb`. */ -cached -private predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = - idominance(exitBB/1, predBB/2)(_, dom, bb) - -/** - * A basic block, that is, a maximal straight-line sequence of control flow nodes - * without branches or joins. - * - * At the database level, a basic block is represented by its first control flow node. - */ -class BasicBlock extends TControlFlowNode { - BasicBlock() { startsBB(this) } - - /** Gets a basic block succeeding this one. */ - BasicBlock getASuccessor() { succBB(this, result) } - - /** Gets a basic block preceding this one. */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } - - /** Gets a node in this block. */ - ControlFlow::Node getANode() { result = getNode(_) } - - /** Gets the node at the given position in this block. */ - ControlFlow::Node getNode(int pos) { bbIndex(this, result, pos) } - - /** Gets the first node in this block. */ - ControlFlow::Node getFirstNode() { result = this } - - /** Gets the last node in this block. */ - ControlFlow::Node getLastNode() { result = getNode(length() - 1) } - - /** Gets the length of this block. */ - int length() { result = bbLength(this) } - - /** Gets the basic block that immediately dominates this basic block. */ - ReachableBasicBlock getImmediateDominator() { bbIDominates(result, this) } - - /** Gets the innermost function or file to which this basic block belongs. */ - ControlFlow::Root getRoot() { result = getFirstNode().getRoot() } - - /** Gets a textual representation of this basic block. */ - string toString() { result = "basic block" } - - /** - * Holds if this basic block is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - getFirstNode().hasLocationInfo(filepath, startline, startcolumn, _, _) and - getLastNode().hasLocationInfo(_, _, _, endline, endcolumn) - } -} - -/** - * An entry basic block, that is, a basic block whose first node is an entry node. - */ -class EntryBasicBlock extends BasicBlock { - EntryBasicBlock() { entryBB(this) } -} - -/** - * A basic block that is reachable from an entry basic block. - */ -class ReachableBasicBlock extends BasicBlock { - ReachableBasicBlock() { reachableBB(this) } - - /** - * Holds if this basic block strictly dominates `bb`. - */ - cached - predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) } - - /** - * Holds if this basic block dominates `bb`. - * - * This predicate is reflexive: each reachable basic block dominates itself. - */ - predicate dominates(ReachableBasicBlock bb) { - bb = this or - strictlyDominates(bb) - } - - /** - * Holds if this basic block strictly post-dominates `bb`. - */ - cached - predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) } - - /** - * Holds if this basic block post-dominates `bb`. - * - * This predicate is reflexive: each reachable basic block post-dominates itself. - */ - predicate postDominates(ReachableBasicBlock bb) { - bb = this or - strictlyPostDominates(bb) - } -} - -/** - * A reachable basic block with more than one predecessor. - */ -class ReachableJoinBlock extends ReachableBasicBlock { - ReachableJoinBlock() { getFirstNode().isJoin() } - - /** - * Holds if this basic block belongs to the dominance frontier of `b`, that is - * `b` dominates a predecessor of this block, but not this block itself. - * - * Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5), - * who in turn attribute it to Ferrante et al., "The program dependence graph and - * its use in optimization". - */ - predicate inDominanceFrontierOf(ReachableBasicBlock b) { - b = getAPredecessor() and not b = getImmediateDominator() - or - exists(ReachableBasicBlock prev | inDominanceFrontierOf(prev) | - b = prev.getImmediateDominator() and - not b = getImmediateDominator() - ) - } -} diff --git a/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll b/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll deleted file mode 100644 index 51e03de3a..000000000 --- a/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll +++ /dev/null @@ -1,290 +0,0 @@ -/** - * Provides classes for working with a CFG-based program representation. - */ - -import go -private import ControlFlowGraphImpl - -/** Provides helper predicates for mapping btween CFG nodes and the AST. */ -module ControlFlow { - /** A file or function with which a CFG is associated. */ - class Root extends AstNode { - Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) } - - /** Holds if `nd` belongs to this file or function. */ - predicate isRootOf(AstNode nd) { - this = nd.getEnclosingFunction() - or - not exists(nd.getEnclosingFunction()) and - this = nd.getFile() - } - - /** Gets the synthetic entry node of the CFG for this file or function. */ - EntryNode getEntryNode() { result = ControlFlow::entryNode(this) } - - /** Gets the synthetic exit node of the CFG for this file or function. */ - ExitNode getExitNode() { result = ControlFlow::exitNode(this) } - } - - /** - * A node in the intra-procedural control-flow graph of a Go function or file. - * - * Nodes correspond to expressions and statements that compute a value or perform - * an operation (as opposed to providing syntactic structure or type information). - * - * There are also synthetic entry and exit nodes for each Go function and file - * that mark the beginning and the end, respectively, of the execution of the - * function and the loading of the file. - */ - class Node extends TControlFlowNode { - /** Gets a node that directly follows this one in the control-flow graph. */ - Node getASuccessor() { result = CFG::succ(this) } - - /** Gets a node that directly precedes this one in the control-flow graph. */ - Node getAPredecessor() { this = result.getASuccessor() } - - /** Holds if this is a node with more than one successor. */ - predicate isBranch() { strictcount(getASuccessor()) > 1 } - - /** Holds if this is a node with more than one predecessor. */ - predicate isJoin() { strictcount(getAPredecessor()) > 1 } - - /** Holds if this is the first control-flow node in `subtree`. */ - predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) } - - /** Holds if this node is the (unique) entry node of a function or file. */ - predicate isEntryNode() { this instanceof MkEntryNode } - - /** Holds if this node is the (unique) exit node of a function or file. */ - predicate isExitNode() { this instanceof MkExitNode } - - /** Gets the basic block to which this node belongs. */ - BasicBlock getBasicBlock() { result.getANode() = this } - - /** Holds if this node dominates `dominee` in the control-flow graph. */ - pragma[inline] - predicate dominatesNode(ControlFlow::Node dominee) { - exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j | - this = thisbb.getNode(i) and dominee = dbb.getNode(j) - | - thisbb.strictlyDominates(dbb) - or - thisbb = dbb and i <= j - ) - } - - /** Gets the innermost function or file to which this node belongs. */ - Root getRoot() { none() } - - /** Gets the file to which this node belongs. */ - File getFile() { hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } - - /** - * Gets a textual representation of this control flow node. - */ - string toString() { result = "control-flow node" } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - } - - /** - * A control-flow node that initializes or updates the value of a constant, a variable, - * a field, or an (array, slice, or map) element. - */ - class WriteNode extends Node { - IR::WriteInstruction self; - - WriteNode() { this = self } - - /** Gets the left-hand side of this write. */ - IR::WriteTarget getLhs() { result = self.getLhs() } - - /** Gets the right-hand side of this write. */ - DataFlow::Node getRhs() { self.getRhs() = result.asInstruction() } - - /** Holds if this node sets variable or constant `v` to `rhs`. */ - predicate writes(ValueEntity v, DataFlow::Node rhs) { self.writes(v, rhs.asInstruction()) } - - /** Holds if this node defines SSA variable `v` to be `rhs`. */ - predicate definesSsaVariable(SsaVariable v, DataFlow::Node rhs) { - self.getLhs().asSsaVariable() = v and - self.getRhs() = rhs.asInstruction() - } - - /** - * Holds if this node sets the value of field `f` on `base` (or its implicit dereference) to - * `rhs`. - * - * For example, for the assignment `x.width = newWidth`, `base` is either the data-flow node - * corresponding to `x` or (if `x` is a pointer) the data-flow node corresponding to the - * implicit dereference `*x`, `f` is the field referenced by `width`, and `rhs` is the data-flow - * node corresponding to `newWidth`. - */ - predicate writesField(DataFlow::Node base, Field f, DataFlow::Node rhs) { - exists(IR::FieldTarget trg | trg = self.getLhs() | - ( - trg.getBase() = base.asInstruction() or - trg.getBase() = MkImplicitDeref(base.asExpr()) - ) and - trg.getField() = f and - self.getRhs() = rhs.asInstruction() - ) - } - - /** - * Holds if this node sets the value of element `index` on `base` (or its implicit dereference) - * to `rhs`. - * - * For example, for the assignment `xs[i] = v`, `base` is either the data-flow node - * corresponding to `xs` or (if `xs` is a pointer) the data-flow node corresponding to the - * implicit dereference `*xs`, `index` is the data-flow node corresponding to `i`, and `rhs` - * is the data-flow node corresponding to `base`. - */ - predicate writesElement(DataFlow::Node base, DataFlow::Node index, DataFlow::Node rhs) { - exists(IR::ElementTarget trg | trg = self.getLhs() | - ( - trg.getBase() = base.asInstruction() or - trg.getBase() = MkImplicitDeref(base.asExpr()) - ) and - trg.getIndex() = index.asInstruction() and - self.getRhs() = rhs.asInstruction() - ) - } - - /** - * Holds if this node sets any field or element of `base` to `rhs`. - */ - predicate writesComponent(DataFlow::Node base, DataFlow::Node rhs) { - writesElement(base, _, rhs) or writesField(base, _, rhs) - } - } - - /** - * A control-flow node recording the fact that a certain expression has a known - * Boolean value at this point in the program. - */ - class ConditionGuardNode extends IR::Instruction, MkConditionGuardNode { - Expr cond; - boolean outcome; - - ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) } - - private predicate ensuresAux(Expr expr, boolean b) { - expr = cond and b = outcome - or - expr = any(ParenExpr par | ensuresAux(par, b)).getExpr() - or - expr = any(NotExpr ne | ensuresAux(ne, b.booleanNot())).getOperand() - or - expr = any(LandExpr land | ensuresAux(land, true)).getAnOperand() and - b = true - or - expr = any(LorExpr lor | ensuresAux(lor, false)).getAnOperand() and - b = false - } - - /** Holds if this guard ensures that the result of `nd` is `b`. */ - predicate ensures(DataFlow::Node nd, boolean b) { - ensuresAux(any(Expr e | nd = DataFlow::exprNode(e)), b) - } - - /** Holds if this guard ensures that `lesser <= greater + bias` holds. */ - predicate ensuresLeq(DataFlow::Node lesser, DataFlow::Node greater, int bias) { - exists(DataFlow::RelationalComparisonNode rel, boolean b | - ensures(rel, b) and - rel.leq(b, lesser, greater, bias) - ) - or - ensuresEq(lesser, greater) and - bias = 0 - } - - /** Holds if this guard ensures that `i = j` holds. */ - predicate ensuresEq(DataFlow::Node i, DataFlow::Node j) { - exists(DataFlow::EqualityTestNode eq, boolean b | - ensures(eq, b) and - eq.eq(b, i, j) - ) - } - - /** Holds if this guard ensures that `i != j` holds. */ - predicate ensuresNeq(DataFlow::Node i, DataFlow::Node j) { - exists(DataFlow::EqualityTestNode eq, boolean b | - ensures(eq, b.booleanNot()) and - eq.eq(b, i, j) - ) - } - - /** - * Holds if this guard dominates basic block `bb`, that is, the guard - * is known to hold at `bb`. - */ - predicate dominates(ReachableBasicBlock bb) { - this = bb.getANode() or - dominates(bb.getImmediateDominator()) - } - - /** - * Gets the condition whose outcome the guard concerns. - */ - Expr getCondition() { result = cond } - - override Root getRoot() { result.isRootOf(cond) } - - override string toString() { result = cond + " is " + outcome } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - cond.hasLocationInfo(filepath, _, _, startline, startcolumn) and - endline = startline and - endcolumn = startcolumn - } - } - - /** - * Gets the entry node of function or file `root`. - */ - Node entryNode(Root root) { result = MkEntryNode(root) } - - /** - * Gets the exit node of function or file `root`. - */ - Node exitNode(Root root) { result = MkExitNode(root) } - - /** - * Holds if the function `f` may return without panicking, exiting the process, or looping forever. - * - * This is defined conservatively, and so may also hold of a function that in fact - * cannot return normally, but never fails to hold of a function that can return normally. - */ - predicate mayReturnNormally(FuncDecl f) { CFG::mayReturnNormally(f.getBody()) } - - /** - * Holds if `pred` is the node for the case `testExpr` in an expression - * switch statement which is switching on `switchExpr`, and `succ` is the - * node to be executed next if the case test succeeds. - */ - predicate isSwitchCaseTestPassingEdge( - ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr - ) { - CFG::isSwitchCaseTestPassingEdge(pred, succ, switchExpr, testExpr) - } -} - -class Write = ControlFlow::WriteNode; diff --git a/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll deleted file mode 100644 index 25137f75a..000000000 --- a/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ /dev/null @@ -1,2080 +0,0 @@ -/** - * INTERNAL: Analyses should use module `ControlFlowGraph` instead. - * - * Provides predicates for building intra-procedural CFGs. - */ - -import go - -/** A block statement that is not the body of a `switch` or `select` statement. */ -class PlainBlock extends BlockStmt { - PlainBlock() { - not this = any(SwitchStmt sw).getBody() and not this = any(SelectStmt sel).getBody() - } -} - -private predicate notBlankIdent(Expr e) { not e instanceof BlankIdent } - -private predicate pureLvalue(ReferenceExpr e) { not e.isRvalue() } - -/** - * Holds if `e` is a branch condition, including the LHS of a short-circuiting binary operator. - */ -private predicate isCondRoot(Expr e) { - e = any(LogicalBinaryExpr lbe).getLeftOperand() - or - e = any(ForStmt fs).getCond() - or - e = any(IfStmt is).getCond() - or - e = any(ExpressionSwitchStmt ess | not exists(ess.getExpr())).getACase().getAnExpr() -} - -/** - * Holds if `e` is a branch condition or part of a logical binary expression contributing to a - * branch condition. - * - * For example, in `v := (x && y) || (z && w)`, `x` and `(x && y)` and `z` are branch conditions - * (`isCondRoot` holds of them), whereas this predicate also holds of `y` (contributes to condition - * `x && y`) but not of `w` (contributes to the value `v`, but not to any branch condition). - * - * In the context `if (x && y) || (z && w)` then the whole `(x && y) || (z && w)` is a branch condition - * as well as `x` and `(x && y)` and `z` as previously, and this predicate holds of all their - * subexpressions. - */ -private predicate isCond(Expr e) { - isCondRoot(e) or - e = any(LogicalBinaryExpr lbe | isCond(lbe)).getRightOperand() or - e = any(ParenExpr par | isCond(par)).getExpr() -} - -/** - * Holds if `e` implicitly reads the embedded field `implicitField`. - * - * The `index` is the distance from the promoted field. For example, if `A` contains an embedded - * field `B`, `B` contains an embedded field `C` and `C` contains the non-embedded field `x`. - * Then `a.x` implicitly reads `C` with index 1 and `B` with index 2. - */ -private predicate implicitFieldSelectionForField(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedField child, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - ( - e.refersTo(child) - or - implicitFieldSelectionForField(e, implicitFieldDepth + 1, child) - ) - | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - exists(PromotedField explicitField, int explicitFieldDepth | - e.refersTo(explicitField) and baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField - | - index = explicitFieldDepth - implicitFieldDepth - ) - ) -} - -private predicate implicitFieldSelectionForMethod(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedMethod method, int mDepth, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - e.refersTo(method) and - baseType.getMethodAtDepth(_, mDepth) = method and - index = mDepth - implicitFieldDepth - | - method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1) - or - exists(PromotedField child | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - implicitFieldSelectionForMethod(e, implicitFieldDepth + 1, child) - ) - ) -} - -/** - * A node in the intra-procedural control-flow graph of a Go function or file. - * - * There are two kinds of control-flow nodes: - * - * 1. Instructions: these are nodes that correspond to expressions and statements - * that compute a value or perform an operation (as opposed to providing syntactic - * structure or type information). - * 2. Synthetic nodes: - * - Entry and exit nodes for each Go function and file that mark the beginning and the end, - * respectively, of the execution of the function and the loading of the file; - * - Skip nodes that are semantic no-ops, but make CFG construction easier. - */ -cached -newtype TControlFlowNode = - /** - * A control-flow node that represents the evaluation of an expression. - */ - MkExprNode(Expr e) { CFG::hasEvaluationNode(e) } or - /** - * A control-flow node that represents the initialization of an element of a composite literal. - */ - MkLiteralElementInitNode(Expr e) { e = any(CompositeLit lit).getAnElement() } or - /** - * A control-flow node that represents the implicit index of an element in a slice or array literal. - */ - MkImplicitLiteralElementIndex(Expr e) { - exists(CompositeLit lit | not lit instanceof StructLit | - e = lit.getAnElement() and - not e instanceof KeyValueExpr - ) - } or - /** - * A control-flow node that represents a (single) assignment. - * - * Assignments with multiple left-hand sides are split up into multiple assignment nodes, - * one for each left-hand side. Assignments to `_` are not represented in the control-flow graph. - */ - MkAssignNode(AstNode assgn, int i) { - // the `i`th assignment in a (possibly multi-)assignment - notBlankIdent(assgn.(Assignment).getLhs(i)) - or - // the `i`th name declared in a (possibly multi-)declaration specifier - notBlankIdent(assgn.(ValueSpec).getNameExpr(i)) - or - // the assignment to the "key" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getKey()) and i = 0 - or - // the assignment to the "value" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getValue()) and i = 1 - } or - /** - * A control-flow node that represents the implicit right-hand side of a compound assignment. - * - * For example, the compound assignment `x += 1` has an implicit right-hand side `x + 1`. - */ - MkCompoundAssignRhsNode(CompoundAssignStmt assgn) or - /** - * A control-flow node that represents the `i`th component of a tuple expression `s`. - */ - MkExtractNode(AstNode s, int i) { - // in an assignment `x, y, z = tuple` - exists(Assignment assgn | - s = assgn and - exists(assgn.getRhs()) and - assgn.getNumLhs() > 1 and - exists(assgn.getLhs(i)) - ) - or - // in a declaration `var x, y, z = tuple` - exists(ValueSpec spec | - s = spec and - exists(spec.getInit()) and - spec.getNumName() > 1 and - exists(spec.getNameExpr(i)) - ) - or - // in a `range` statement - exists(RangeStmt rs | s = rs | - exists(rs.getKey()) and i = 0 - or - exists(rs.getValue()) and i = 1 - ) - or - // in a return statement `return f()` where `f` has multiple return values - exists(ReturnStmt ret, SignatureType rettp | - s = ret and - // the return statement has a single expression - exists(ret.getExpr()) and - // but the enclosing function has multiple results - rettp = ret.getEnclosingFunction().getType() and - rettp.getNumResult() > 1 and - exists(rettp.getResultType(i)) - ) - or - // in a call `f(g())` where `g` has multiple return values - exists(CallExpr outer, CallExpr inner | s = outer | - inner = outer.getArgument(0).stripParens() and - outer.getNumArgument() = 1 and - exists(inner.getType().(TupleType).getComponentType(i)) - ) - } or - /** - * A control-flow node that represents the zero value to which a variable without an initializer - * expression is initialized. - */ - MkZeroInitNode(ValueEntity v) { - exists(ValueSpec spec, int i | - not exists(spec.getAnInit()) and - spec.getNameExpr(i) = v.getDeclaration() - ) - or - exists(v.(ResultVariable).getFunction().getBody()) - } or - /** - * A control-flow node that represents a function declaration. - */ - MkFuncDeclNode(FuncDecl fd) or - /** - * A control-flow node that represents a `defer` statement. - */ - MkDeferNode(DeferStmt def) or - /** - * A control-flow node that represents a `go` statement. - */ - MkGoNode(GoStmt go) or - /** - * A control-flow node that represents the fact that `e` is known to evaluate to - * `outcome`. - */ - MkConditionGuardNode(Expr e, Boolean outcome) { isCondRoot(e) } or - /** - * A control-flow node that represents an increment or decrement statement. - */ - MkIncDecNode(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit right-hand side of an increment or decrement statement. - */ - MkIncDecRhs(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit operand 1 of an increment or decrement statement. - */ - MkImplicitOne(IncDecStmt ids) or - /** - * A control-flow node that represents a return from a function. - */ - MkReturnNode(ReturnStmt ret) or - /** - * A control-flow node that represents the implicit write to a named result variable in a return statement. - */ - MkResultWriteNode(ResultVariable var, int i, ReturnStmt ret) { - ret.getEnclosingFunction().getResultVar(i) = var and - exists(ret.getAnExpr()) - } or - /** - * A control-flow node that represents the implicit read of a named result variable upon returning from - * a function (after any deferred calls have been executed). - */ - MkResultReadNode(ResultVariable var) or - /** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than potentially - * influencing control flow: the branching statements `continue`, `break`, `fallthrough` and `goto`; empty - * blocks; empty statements; and import and type declarations. - */ - MkSkipNode(AstNode skip) { - skip instanceof BranchStmt - or - skip instanceof EmptyStmt - or - skip.(PlainBlock).getNumStmt() = 0 - or - skip instanceof ImportDecl - or - skip instanceof TypeDecl - or - pureLvalue(skip) - or - skip.(CaseClause).getNumStmt() = 0 - or - skip.(CommClause).getNumStmt() = 0 - } or - /** - * A control-flow node that represents a `select` operation. - */ - MkSelectNode(SelectStmt sel) or - /** - * A control-flow node that represents a `send` operation. - */ - MkSendNode(SendStmt send) or - /** - * A control-flow node that represents the initialization of a parameter to its corresponding argument. - */ - MkParameterInit(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the argument corresponding to a parameter. - */ - MkArgumentNode(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the initialization of a result variable to its zero value. - */ - MkResultInit(ResultVariable rv) { exists(rv.getFunction().getBody()) } or - /** - * A control-flow node that represents the operation of retrieving the next (key, value) pair in a - * `range` statement, if any. - */ - MkNextNode(RangeStmt rs) or - /** - * A control-flow node that represents the implicit `true` expression in `switch { ... }`. - */ - MkImplicitTrue(ExpressionSwitchStmt stmt) { not exists(stmt.getExpr()) } or - /** - * A control-flow node that represents the implicit comparison or type check performed by - * the `i`th expression of a case clause `cc`. - */ - MkCaseCheckNode(CaseClause cc, int i) { exists(cc.getExpr(i)) } or - /** - * A control-flow node that represents the implicit lower bound of a slice expression. - */ - MkImplicitLowerSliceBound(SliceExpr sl) { not exists(sl.getLow()) } or - /** - * A control-flow node that represents the implicit upper bound of a simple slice expression. - */ - MkImplicitUpperSliceBound(SliceExpr sl) { not exists(sl.getHigh()) } or - /** - * A control-flow node that represents the implicit max bound of a simple slice expression. - */ - MkImplicitMaxSliceBound(SliceExpr sl) { not exists(sl.getMax()) } or - /** - * A control-flow node that represents the implicit dereference of the base in a field/method - * access, element access, or slice expression. - */ - MkImplicitDeref(Expr e) { - e.getType().getUnderlyingType() instanceof PointerType and - ( - exists(SelectorExpr sel | e = sel.getBase() | - // field accesses through a pointer always implicitly dereference - sel = any(Field f).getAReference() - or - // method accesses only dereference if the receiver is _not_ a pointer - exists(Method m, Type tp | - sel = m.getAReference() and - tp = m.getReceiver().getType().getUnderlyingType() and - not tp instanceof PointerType - ) - ) - or - e = any(IndexExpr ie).getBase() - or - e = any(SliceExpr se).getBase() - ) - } or - /** - * A control-flow node that represents the implicit selection of a field when - * accessing a promoted field. - * - * If that field has a pointer type then this control-flow node also - * represents an implicit dereference of it. - */ - MkImplicitFieldSelection(PromotedSelector e, int i, Field implicitField) { - implicitFieldSelectionForField(e, i, implicitField) or - implicitFieldSelectionForMethod(e, i, implicitField) - } or - /** - * A control-flow node that represents the start of the execution of a function or file. - */ - MkEntryNode(ControlFlow::Root root) or - /** - * A control-flow node that represents the end of the execution of a function or file. - */ - MkExitNode(ControlFlow::Root root) - -/** A representation of the target of a write. */ -newtype TWriteTarget = - /** A write target that is represented explicitly in the AST. */ - MkLhs(TControlFlowNode write, Expr lhs) { - exists(AstNode assgn, int i | write = MkAssignNode(assgn, i) | - lhs = assgn.(Assignment).getLhs(i).stripParens() - or - lhs = assgn.(ValueSpec).getNameExpr(i) - or - exists(RangeStmt rs | rs = assgn | - i = 0 and lhs = rs.getKey().stripParens() - or - i = 1 and lhs = rs.getValue().stripParens() - ) - ) - or - exists(IncDecStmt ids | write = MkIncDecNode(ids) | lhs = ids.getOperand().stripParens()) - or - exists(Parameter parm | write = MkParameterInit(parm) | lhs = parm.getDeclaration()) - or - exists(ResultVariable res | write = MkResultInit(res) | lhs = res.getDeclaration()) - } or - /** A write target for an element in a compound literal, viewed as a field write. */ - MkLiteralElementTarget(MkLiteralElementInitNode elt) or - /** A write target for a returned expression, viewed as a write to the corresponding result variable. */ - MkResultWriteTarget(MkResultWriteNode w) - -/** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than - * potentially influencing control flow: the branching statements `continue`, `break`, - * `fallthrough` and `goto`; empty blocks; empty statements; and import and type declarations. - */ -class SkipNode extends ControlFlow::Node, MkSkipNode { - AstNode skip; - - SkipNode() { this = MkSkipNode(skip) } - - override ControlFlow::Root getRoot() { result.isRootOf(skip) } - - override string toString() { result = "skip" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - skip.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * A control-flow node that represents the start of the execution of a function or file. - */ -class EntryNode extends ControlFlow::Node, MkEntryNode { - ControlFlow::Root root; - - EntryNode() { this = MkEntryNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "entry" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - root.hasLocationInfo(filepath, startline, startcolumn, _, _) and - endline = startline and - endcolumn = startcolumn - } -} - -/** - * A control-flow node that represents the end of the execution of a function or file. - */ -class ExitNode extends ControlFlow::Node, MkExitNode { - ControlFlow::Root root; - - ExitNode() { this = MkExitNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "exit" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - root.hasLocationInfo(filepath, _, _, endline, endcolumn) and - endline = startline and - endcolumn = startcolumn - } -} - -/** - * Provides classes and predicates for computing the control-flow graph. - */ -cached -module CFG { - /** - * The target of a branch statement, which is either the label of a labeled statement or - * the special target `""` referring to the innermost enclosing loop or `switch`. - */ - private class BranchTarget extends string { - BranchTarget() { this = any(LabeledStmt ls).getLabel() or this = "" } - } - - private module BranchTarget { - /** Holds if this is the target of branch statement `stmt` or the label of compound statement `stmt`. */ - BranchTarget of(Stmt stmt) { - exists(BranchStmt bs | bs = stmt | - result = bs.getLabel() - or - not exists(bs.getLabel()) and result = "" - ) - or - exists(LabeledStmt ls | stmt = ls.getStmt() | result = ls.getLabel()) - or - (stmt instanceof LoopStmt or stmt instanceof SwitchStmt or stmt instanceof SelectStmt) and - result = "" - } - } - - private newtype TCompletion = - /** A completion indicating that an expression or statement was evaluated successfully. */ - Done() or - /** - * A completion indicating that an expression was successfully evaluated to Boolean value `b`. - * - * Note that many Boolean expressions are modeled as having completion `Done()` instead. - * Completion `Bool` is only used in contexts where the Boolean value can be determined. - */ - Bool(boolean b) { b = true or b = false } or - /** - * A completion indicating that execution of a (compound) statement ended with a `break` - * statement targeting the given label. - */ - Break(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `continue` - * statement targeting the given label. - */ - Continue(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `fallthrough` - * statement. - */ - Fallthrough() or - /** - * A completion indicating that execution of a (compound) statement ended with a `return` - * statement. - */ - Return() or - /** - * A completion indicating that execution of a statement or expression may have ended with - * a panic being raised. - */ - Panic() - - private Completion normalCompletion() { result.isNormal() } - - private class Completion extends TCompletion { - predicate isNormal() { this = Done() or this = Bool(_) } - - Boolean getOutcome() { this = Done() or this = Bool(result) } - - string toString() { - this = Done() and result = "normal" - or - exists(boolean b | this = Bool(b) | result = b.toString()) - or - exists(BranchTarget lbl | - this = Break(lbl) and result = "break " + lbl - or - this = Continue(lbl) and result = "continue " + lbl - ) - or - this = Fallthrough() and result = "fallthrough" - or - this = Return() and result = "return" - or - this = Panic() and result = "panic" - } - } - - /** - * Holds if `e` should have an evaluation node in the control-flow graph. - * - * Excluded expressions include those not evaluated at runtime (e.g. identifiers, type expressions) - * and some logical expressions that are expressed as control-flow edges rather than having a specific - * evaluation node. - */ - cached - predicate hasEvaluationNode(Expr e) { - // exclude expressions that do not denote a value - not e instanceof TypeExpr and - not e = any(FieldDecl f).getTag() and - not e instanceof KeyValueExpr and - not e = any(SelectorExpr sel).getSelector() and - not e = any(StructLit sl).getKey(_) and - not (e instanceof Ident and not e instanceof ReferenceExpr) and - not (e instanceof SelectorExpr and not e instanceof ReferenceExpr) and - not pureLvalue(e) and - // exclude parentheses, which are purely concrete syntax, and some logical binary expressions - // whose evaluation is implied by control-flow edges without requiring an evaluation node. - not isControlFlowStructural(e) and - // exclude expressions that are not evaluated at runtime - not e = any(ImportSpec is).getPathExpr() and - not e.getParent*() = any(ArrayTypeExpr ate).getLength() and - // sub-expressions of constant expressions are not evaluated (even if they don't look constant - // themselves) - not constRoot(e.getParent+()) - } - - /** - * Holds if `e` is an expression that purely serves grouping or control-flow purposes. - * - * Examples include parenthesized expressions and short-circuiting Boolean expressions used within - * a branch condition (`if` or `for` condition, or as part of a larger boolean expression, e.g. - * in `(x && y) || z`, the `&&` subexpression matches this predicate). - */ - private predicate isControlFlowStructural(Expr e) { - // Some logical binary operators do not need an evaluation node - // (for example, in `if x && y`, we evaluate `x` and then branch straight to either `y` or the - // `else` block, so there is no control-flow step where `x && y` is specifically calculated) - e instanceof LogicalBinaryExpr and - isCond(e) - or - // Purely concrete-syntactic structural expression: - e instanceof ParenExpr - } - - /** - * Gets a constant root, that is, an expression that is constant but whose parent expression is not. - * - * As an exception to the latter, for a control-flow structural expression such as `(c1)` or `c1 && c2` - * where `cn` are constants we still consider the `cn`s to be a constant roots, even though their parent - * expression is also constant. - */ - private predicate constRoot(Expr root) { - exists(Expr c | - c.isConst() and - not c.getParent().(Expr).isConst() and - root = stripStructural(c) - ) - } - - /** - * Strips off any control-flow structural components from `e`. - */ - private Expr stripStructural(Expr e) { - if isControlFlowStructural(e) then result = stripStructural(e.getAChildExpr()) else result = e - } - - private class ControlFlowTree extends AstNode { - predicate firstNode(ControlFlow::Node first) { none() } - - predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // propagate abnormal completion from children - lastNode(this.getAChild(), last, cmpl) and - not cmpl.isNormal() - } - - predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getChildTreeRanked(i), pred, normalCompletion()) and - firstNode(this.getChildTreeRanked(i + 1), succ) - ) - } - - final ControlFlowTree getChildTreeRanked(int i) { - exists(int j | - result = this.getChildTree(j) and - j = rank[i + 1](int k | exists(this.getChildTree(k))) - ) - } - - ControlFlowTree getFirstChildTree() { result = this.getChildTreeRanked(0) } - - ControlFlowTree getLastChildTree() { - result = max(ControlFlowTree ch, int j | ch = this.getChildTree(j) | ch order by j) - } - - ControlFlowTree getChildTree(int i) { none() } - } - - private class AtomicTree extends ControlFlowTree { - ControlFlow::Node nd; - Completion cmpl; - - AtomicTree() { - exists(Expr e | - e = this and - e.isConst() and - nd = mkExprOrSkipNode(this) - | - if e.isPlatformIndependentConstant() and exists(e.getBoolValue()) - then cmpl = Bool(e.getBoolValue()) - else cmpl = Done() - ) - or - this instanceof Ident and - not this.(Expr).isConst() and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof BreakStmt and - nd = MkSkipNode(this) and - cmpl = Break(BranchTarget::of(this)) - or - this instanceof ContinueStmt and - nd = MkSkipNode(this) and - cmpl = Continue(BranchTarget::of(this)) - or - this instanceof Decl and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof EmptyStmt and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof FallthroughStmt and - nd = MkSkipNode(this) and - cmpl = Fallthrough() - or - this instanceof FuncLit and - nd = MkExprNode(this) and - cmpl = Done() - or - this instanceof PlainBlock and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof SelectorExpr and - not this.(SelectorExpr).getBase() instanceof ValueExpr and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof GenericFunctionInstantiationExpr and - nd = MkExprNode(this) and - cmpl = Done() - } - - override predicate firstNode(ControlFlow::Node first) { first = nd } - - override predicate lastNode(ControlFlow::Node last, Completion c) { last = nd and c = cmpl } - } - - abstract private class PostOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - Completion getCompletion() { result = Done() } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - or - not exists(this.getChildTree(_)) and - first = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - last = this.getNode() and cmpl = this.getCompletion() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ(pred, succ) - or - lastNode(this.getLastChildTree(), pred, normalCompletion()) and - succ = this.getNode() - } - } - - abstract private class PreOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - override predicate firstNode(ControlFlow::Node first) { first = this.getNode() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - not exists(this.getChildTree(_)) and - last = this.getNode() and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ(pred, succ) - or - pred = this.getNode() and - firstNode(this.getFirstChildTree(), succ) - } - } - - private class WrapperTree extends ControlFlowTree { - WrapperTree() { - this instanceof ConstDecl or - this instanceof DeclStmt or - this instanceof ExprStmt or - this instanceof KeyValueExpr or - this instanceof LabeledStmt or - this instanceof ParenExpr or - this instanceof PlainBlock or - this instanceof VarDecl - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - exists(LoopStmt ls | this = ls.getBody() | - lastNode(this, last, Continue(BranchTarget::of(ls))) and - cmpl = Done() - ) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.(DeclStmt).getDecl() - or - i = 0 and result = this.(ExprStmt).getExpr() - or - result = this.(GenDecl).getSpec(i) - or - exists(KeyValueExpr kv | kv = this | - not kv.getLiteral() instanceof StructLit and - i = 0 and - result = kv.getKey() - or - i = 1 and result = kv.getValue() - ) - or - i = 0 and result = this.(LabeledStmt).getStmt() - or - i = 0 and result = this.(ParenExpr).getExpr() - or - result = this.(PlainBlock).getStmt(i) - } - } - - private class AssignmentTree extends ControlFlowTree { - AssignmentTree() { - this instanceof Assignment or - this instanceof ValueSpec - } - - Expr getLhs(int i) { - result = this.(Assignment).getLhs(i) or - result = this.(ValueSpec).getNameExpr(i) - } - - int getNumLhs() { - result = this.(Assignment).getNumLhs() or - result = this.(ValueSpec).getNumName() - } - - Expr getRhs(int i) { - result = this.(Assignment).getRhs(i) or - result = this.(ValueSpec).getInit(i) - } - - int getNumRhs() { - result = this.(Assignment).getNumRhs() or - result = this.(ValueSpec).getNumInit() - } - - predicate isExtractingAssign() { this.getNumRhs() = 1 and this.getNumLhs() > 1 } - - override predicate firstNode(ControlFlow::Node first) { - not this instanceof RecvStmt and - firstNode(this.getLhs(0), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - ( - last = max(int i | | this.epilogueNode(i) order by i) - or - not exists(this.epilogueNode(_)) and - lastNode(this.getLastSubExprInEvalOrder(), last, normalCompletion()) - ) and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - exists(int i | lastNode(this.getLhs(i), pred, normalCompletion()) | - firstNode(this.getLhs(i + 1), succ) - or - not this instanceof RecvStmt and - i = this.getNumLhs() - 1 and - ( - firstNode(this.getRhs(0), succ) - or - not exists(this.getRhs(_)) and - succ = this.epilogueNodeRanked(0) - ) - ) - or - exists(int i | - lastNode(this.getRhs(i), pred, normalCompletion()) and - firstNode(this.getRhs(i + 1), succ) - ) - or - not this instanceof RecvStmt and - lastNode(this.getRhs(this.getNumRhs() - 1), pred, normalCompletion()) and - succ = this.epilogueNodeRanked(0) - or - exists(int i | - pred = this.epilogueNodeRanked(i) and - succ = this.epilogueNodeRanked(i + 1) - ) - } - - ControlFlow::Node epilogueNodeRanked(int i) { - exists(int j | - result = this.epilogueNode(j) and - j = rank[i + 1](int k | exists(this.epilogueNode(k))) - ) - } - - private Expr getSubExprInEvalOrder(int evalOrder) { - if evalOrder < this.getNumLhs() - then result = this.getLhs(evalOrder) - else result = this.getRhs(evalOrder - this.getNumLhs()) - } - - private Expr getLastSubExprInEvalOrder() { - result = max(int i | | this.getSubExprInEvalOrder(i) order by i) - } - - private ControlFlow::Node epilogueNode(int i) { - i = -1 and - result = MkCompoundAssignRhsNode(this) - or - exists(int j | - result = MkExtractNode(this, j) and - i = 2 * j - or - result = MkZeroInitNode(any(ValueEntity v | this.getLhs(j) = v.getDeclaration())) and - i = 2 * j - or - result = MkAssignNode(this, j) and - i = 2 * j + 1 - ) - } - } - - private class BinaryExprTree extends PostOrderTree, BinaryExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - private predicate equalityTestMayPanic() { - this instanceof EqualityTestExpr and - exists(Type t | - t = this.getAnOperand().getType().getUnderlyingType() and - ( - t instanceof InterfaceType or // panic due to comparison of incomparable interface values - t instanceof StructType or // may contain an interface-typed field - t instanceof ArrayType // may be an array of interface values - ) - ) - } - - override Completion getCompletion() { - result = PostOrderTree.super.getCompletion() - or - // runtime panic due to division by zero or comparison of incomparable interface values - (this instanceof DivExpr or this.equalityTestMayPanic()) and - not this.(Expr).isConst() and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getLeftOperand() - or - i = 1 and result = this.getRightOperand() - } - } - - private class LogicalBinaryExprTree extends BinaryExprTree, LogicalBinaryExpr { - boolean shortCircuit; - - LogicalBinaryExprTree() { - this instanceof LandExpr and shortCircuit = false - or - this instanceof LorExpr and shortCircuit = true - } - - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getLeftOperand(), outcome) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getAnOperand(), last, cmpl) and - not cmpl.isNormal() - or - if isCond(this) - then ( - last = this.getGuard(shortCircuit) and - cmpl = Bool(shortCircuit) - or - lastNode(this.getRightOperand(), last, cmpl) - ) else ( - last = MkExprNode(this) and - cmpl = Done() - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(Completion lcmpl | - lastNode(this.getLeftOperand(), pred, lcmpl) and - succ = this.getGuard(lcmpl.getOutcome()) - ) - or - pred = this.getGuard(shortCircuit.booleanNot()) and - firstNode(this.getRightOperand(), succ) - or - not isCond(this) and - ( - pred = this.getGuard(shortCircuit) and - succ = MkExprNode(this) - or - exists(Completion rcmpl | - lastNode(this.getRightOperand(), pred, rcmpl) and - rcmpl.isNormal() and - succ = MkExprNode(this) - ) - ) - } - } - - private class CallExprTree extends PostOrderTree, CallExpr { - private predicate isSpecial() { - this = any(DeferStmt defer).getCall() or - this = any(GoStmt go).getCall() - } - - override ControlFlow::Node getNode() { - not this.isSpecial() and - result = MkExprNode(this) - } - - override Completion getCompletion() { - (not exists(this.getTarget()) or this.getTarget().mayReturnNormally()) and - result = Done() - or - (not exists(this.getTarget()) or this.getTarget().mayPanic()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getCalleeExpr() - or - result = this.getArgument(i - 1) and - // calls to `make` and `new` can have type expressions as arguments - not result instanceof TypeExpr - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - // interpose implicit argument destructuring nodes between last argument - // and call itself; this is for cases like `f(g())` where `g` has multiple - // results - exists(ControlFlow::Node mid | PostOrderTree.super.succ(pred, mid) | - if mid = this.getNode() then succ = this.getEpilogueNode(0) else succ = mid - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkExtractNode(this, i) - or - i = max(int j | exists(MkExtractNode(this, j))) + 1 and - result = this.getNode() - or - not exists(MkExtractNode(this, _)) and - i = 0 and - result = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - PostOrderTree.super.lastNode(last, cmpl) - or - this.isSpecial() and - lastNode(this.getLastChildTree(), last, cmpl) - } - } - - private class CaseClauseTree extends ControlFlowTree, CaseClause { - private ControlFlow::Node getExprStart(int i) { - firstNode(this.getExpr(i), result) - or - this.getExpr(i) instanceof TypeExpr and - result = MkCaseCheckNode(this, i) - } - - ControlFlow::Node getExprEnd(int i, Boolean outcome) { - exists(Expr e | e = this.getExpr(i) | - result = MkConditionGuardNode(e, outcome) - or - not exists(MkConditionGuardNode(e, _)) and - result = MkCaseCheckNode(this, i) - ) - } - - private ControlFlow::Node getBodyStart() { - firstNode(this.getStmt(0), result) or result = MkSkipNode(this) - } - - override predicate firstNode(ControlFlow::Node first) { - first = this.getExprStart(0) - or - not exists(this.getAnExpr()) and - first = this.getBodyStart() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // TODO: shouldn't be here - last = this.getExprEnd(this.getNumExpr() - 1, false) and - cmpl = Bool(false) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = MkCaseCheckNode(this, i) - or - // visit guard node if there is one - pred = MkCaseCheckNode(this, i) and - succ = this.getExprEnd(i, _) and - succ != pred // this avoids self-loops if there isn't a guard node - or - pred = this.getExprEnd(i, false) and - succ = this.getExprStart(i + 1) - or - this.isPassingEdge(i, pred, succ, _) - ) - } - - predicate isPassingEdge(int i, ControlFlow::Node pred, ControlFlow::Node succ, Expr testExpr) { - pred = this.getExprEnd(i, true) and - succ = this.getBodyStart() and - testExpr = this.getExpr(i) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CommClauseTree extends ControlFlowTree, CommClause { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getComm(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CompositeLiteralTree extends ControlFlowTree, CompositeLit { - private ControlFlow::Node getElementInit(int i) { - result = MkLiteralElementInitNode(this.getElement(i)) - } - - private ControlFlow::Node getElementStart(int i) { - exists(Expr elt | elt = this.getElement(i) | - result = MkImplicitLiteralElementIndex(elt) - or - (elt instanceof KeyValueExpr or this instanceof StructLit) and - firstNode(this.getElement(i), result) - ) - } - - override predicate firstNode(ControlFlow::Node first) { first = MkExprNode(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = this.getElementInit(this.getNumElement() - 1) and - cmpl = Done() - or - not exists(this.getElement(_)) and - last = MkExprNode(this) and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - this.firstNode(pred) and - succ = this.getElementStart(0) - or - exists(int i | - pred = MkImplicitLiteralElementIndex(this.getElement(i)) and - firstNode(this.getElement(i), succ) - or - lastNode(this.getElement(i), pred, normalCompletion()) and - succ = this.getElementInit(i) - or - pred = this.getElementInit(i) and - succ = this.getElementStart(i + 1) - ) - } - } - - private class ConversionExprTree extends PostOrderTree, ConversionExpr { - override Completion getCompletion() { - // conversions of a slice to an array pointer are the only kind that may panic - this.getType().(PointerType).getBaseType() instanceof ArrayType and - result = Panic() - or - result = Done() - } - - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getOperand() } - } - - private class DeferStmtTree extends PostOrderTree, DeferStmt { - override ControlFlow::Node getNode() { result = MkDeferNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class FuncDeclTree extends PostOrderTree, FuncDecl { - override ControlFlow::Node getNode() { result = MkFuncDeclNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getNameExpr() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // override to prevent panic propagation out of function declarations - last = this.getNode() and cmpl = Done() - } - } - - private class GoStmtTree extends PostOrderTree, GoStmt { - override ControlFlow::Node getNode() { result = MkGoNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class IfStmtTree extends ControlFlowTree, IfStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - firstNode(this.getCond(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - lastNode(this.getThen(), last, cmpl) - or - lastNode(this.getElse(), last, cmpl) - or - not exists(this.getElse()) and - last = this.getGuard(false) and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getInit(), pred, normalCompletion()) and - firstNode(this.getCond(), succ) - or - exists(Completion condCmpl | - lastNode(this.getCond(), pred, condCmpl) and - succ = MkConditionGuardNode(this.getCond(), condCmpl.getOutcome()) - ) - or - pred = this.getGuard(true) and - firstNode(this.getThen(), succ) - or - pred = this.getGuard(false) and - firstNode(this.getElse(), succ) - } - } - - private class IndexExprTree extends ControlFlowTree, IndexExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - firstNode(this.getIndex(), succ) - ) - or - pred = MkImplicitDeref(this.getBase()) and - firstNode(this.getIndex(), succ) - or - lastNode(this.getIndex(), pred, normalCompletion()) and - succ = mkExprOrSkipNode(this) - } - } - - private class LoopTree extends ControlFlowTree, LoopStmt { - BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getBody(), last, inner) and not inner.isNormal() | - if inner = Break(this.getLabel()) - then cmpl = Done() - else - if inner = Continue(this.getLabel()) - then none() - else cmpl = inner - ) - } - } - - private class FileTree extends ControlFlowTree, File { - FileTree() { exists(this.getADecl()) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - pred = MkEntryNode(this) and - firstNode(this.getDecl(0), succ) - or - exists(int i, Completion inner | lastNode(this.getDecl(i), pred, inner) | - not inner.isNormal() - or - i = this.getNumDecl() - 1 - ) and - succ = MkExitNode(this) - } - - override ControlFlowTree getChildTree(int i) { result = this.getDecl(i) } - } - - private class ForTree extends LoopTree, ForStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getCond(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getPost(), last, cmpl) and - not cmpl.isNormal() - or - last = this.getGuard(false) and - cmpl = Done() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getInit() - or - i = 1 and result = this.getCond() - or - i = 2 and result = this.getBody() - or - i = 3 and result = this.getPost() - or - i = 4 and result = this.getCond() - or - i = 5 and result = this.getBody() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i, ControlFlowTree predTree, Completion cmpl | - predTree = this.getChildTreeRanked(i) and - lastNode(predTree, pred, cmpl) and - cmpl.isNormal() - | - if predTree = this.getCond() - then succ = this.getGuard(cmpl.getOutcome()) - else firstNode(this.getChildTreeRanked(i + 1), succ) - ) - or - pred = this.getGuard(true) and - firstNode(this.getBody(), succ) - } - } - - private class FuncDefTree extends ControlFlowTree, FuncDef { - FuncDefTree() { exists(this.getBody()) } - - pragma[noinline] - private MkEntryNode getEntry() { result = MkEntryNode(this) } - - private Parameter getParameterRanked(int i) { - result = rank[i + 1](Parameter p, int j | p = this.getParameter(j) | p order by j) - } - - private ControlFlow::Node getPrologueNode(int i) { - i = -1 and result = this.getEntry() - or - exists(int numParm, int numRes | - numParm = count(this.getParameter(_)) and - numRes = count(this.getResultVar(_)) - | - exists(int j, Parameter p | p = this.getParameterRanked(j) | - i = 2 * j and result = MkArgumentNode(p) - or - i = 2 * j + 1 and result = MkParameterInit(p) - ) - or - exists(int j, ResultVariable v | v = this.getResultVar(j) | - i = 2 * numParm + 2 * j and - result = MkZeroInitNode(v) - or - i = 2 * numParm + 2 * j + 1 and - result = MkResultInit(v) - ) - or - i = 2 * numParm + 2 * numRes and - firstNode(this.getBody(), result) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkResultReadNode(this.getResultVar(i)) - or - i = count(this.getAResultVar()) and - result = MkExitNode(this) - } - - pragma[noinline] - private predicate firstDefer(ControlFlow::Node nd) { - exists(DeferStmt defer | - nd = MkExprNode(defer.getCall()) and - // `defer` can be the first `defer` statement executed - // there is always a predecessor node because the `defer`'s call is always - // evaluated before the defer statement itself - MkDeferNode(defer) = succ(notDeferSucc*(this.getEntry())) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - pred = this.getPrologueNode(i) and - succ = this.getPrologueNode(i + 1) - ) - or - exists(GotoStmt goto, LabeledStmt ls | - pred = MkSkipNode(goto) and - this = goto.getEnclosingFunction() and - this = ls.getEnclosingFunction() and - goto.getLabel() = ls.getLabel() and - firstNode(ls, succ) - ) - or - exists(Completion cmpl | - lastNode(this.getBody(), pred, cmpl) and - // last node of function body can be reached without going through a `defer` statement - pred = notDeferSucc*(this.getEntry()) - | - // panic goes directly to exit, non-panic reads result variables first - if cmpl = Panic() then succ = MkExitNode(this) else succ = this.getEpilogueNode(0) - ) - or - lastNode(this.getBody(), pred, _) and - exists(DeferStmt defer | defer = this.getADeferStmt() | - succ = MkExprNode(defer.getCall()) and - // the last `DeferStmt` executed before pred is this `defer` - pred = notDeferSucc*(MkDeferNode(defer)) - ) - or - exists(DeferStmt predDefer, DeferStmt succDefer | - predDefer = this.getADeferStmt() and - succDefer = this.getADeferStmt() - | - // reversed because `defer`s are executed in LIFO order - MkDeferNode(predDefer) = nextDefer(MkDeferNode(succDefer)) and - pred = MkExprNode(predDefer.getCall()) and - succ = MkExprNode(succDefer.getCall()) - ) - or - this.firstDefer(pred) and - ( - // conservatively assume that we might either panic (and hence skip the result reads) - // or not - succ = MkExitNode(this) - or - succ = this.getEpilogueNode(0) - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - } - - private class GotoTree extends ControlFlowTree, GotoStmt { - override predicate firstNode(ControlFlow::Node first) { first = MkSkipNode(this) } - } - - private class IncDecTree extends ControlFlowTree, IncDecStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkIncDecNode(this) and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkImplicitOne(this) - or - pred = MkImplicitOne(this) and - succ = MkIncDecRhs(this) - or - pred = MkIncDecRhs(this) and - succ = MkIncDecNode(this) - } - } - - private class RangeTree extends LoopTree, RangeStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getDomain(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - last = MkNextNode(this) and - cmpl = Done() - or - lastNode(this.getKey(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getValue(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getDomain(), last, cmpl) and - not cmpl.isNormal() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getDomain(), pred, normalCompletion()) and - succ = MkNextNode(this) - or - pred = MkNextNode(this) and - ( - firstNode(this.getKey(), succ) - or - not exists(this.getKey()) and - firstNode(this.getBody(), succ) - ) - or - lastNode(this.getKey(), pred, normalCompletion()) and - ( - firstNode(this.getValue(), succ) - or - not exists(this.getValue()) and - succ = MkExtractNode(this, 0) - ) - or - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkExtractNode(this, 0) - or - pred = MkExtractNode(this, 0) and - ( - if exists(this.getValue()) - then succ = MkExtractNode(this, 1) - else - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkExtractNode(this, 1) and - ( - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 0) and - ( - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 1) and - firstNode(this.getBody(), succ) - or - exists(Completion inner | - lastNode(this.getBody(), pred, inner) and - (inner.isNormal() or inner = Continue(BranchTarget::of(this))) and - succ = MkNextNode(this) - ) - } - } - - private class RecvStmtTree extends ControlFlowTree, RecvStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getExpr().getOperand(), first) - } - } - - private class ReturnStmtTree extends PostOrderTree, ReturnStmt { - override ControlFlow::Node getNode() { result = MkReturnNode(this) } - - override Completion getCompletion() { result = Return() } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = this.complete(i) - or - pred = MkExtractNode(this, i) and - succ = this.after(i) - or - pred = MkResultWriteNode(_, i, this) and - succ = this.next(i) - ) - } - - private ControlFlow::Node complete(int i) { - result = MkExtractNode(this, i) - or - not exists(MkExtractNode(this, _)) and - result = this.after(i) - } - - private ControlFlow::Node after(int i) { - result = MkResultWriteNode(_, i, this) - or - not exists(MkResultWriteNode(_, i, this)) and - result = this.next(i) - } - - private ControlFlow::Node next(int i) { - firstNode(this.getExpr(i + 1), result) - or - exists(MkExtractNode(this, _)) and - result = this.complete(i + 1) - or - i + 1 = this.getEnclosingFunction().getType().getNumResult() and - result = this.getNode() - } - - override ControlFlowTree getChildTree(int i) { result = this.getExpr(i) } - } - - private class SelectStmtTree extends ControlFlowTree, SelectStmt { - private BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getNonDefaultCommClause(0), first) - or - this.getNumNonDefaultCommClause() = 0 and - first = MkSelectNode(this) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getACommClause(), last, inner) | - if inner = Break(this.getLabel()) then cmpl = Done() else cmpl = inner - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - exists(CommClause cc, int i, Stmt comm | - cc = this.getNonDefaultCommClause(i) and - comm = cc.getComm() and - ( - comm instanceof RecvStmt and - lastNode(comm.(RecvStmt).getExpr().getOperand(), pred, normalCompletion()) - or - comm instanceof SendStmt and - lastNode(comm.(SendStmt).getValue(), pred, normalCompletion()) - ) - | - firstNode(this.getNonDefaultCommClause(i + 1), succ) - or - i = this.getNumNonDefaultCommClause() - 1 and - succ = MkSelectNode(this) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc, Stmt comm | - cc = this.getNonDefaultCommClause(_) and comm = cc.getComm() - | - comm instanceof RecvStmt and - succ = MkExprNode(comm.(RecvStmt).getExpr()) - or - comm instanceof SendStmt and - succ = MkSendNode(comm) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc | cc = this.getDefaultCommClause() | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - or - exists(CommClause cc, RecvStmt recv | cc = this.getCommClause(_) and recv = cc.getComm() | - pred = MkExprNode(recv.getExpr()) and - ( - firstNode(recv.getLhs(0), succ) - or - not exists(recv.getLhs(0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(0), pred, normalCompletion()) and - not exists(recv.getLhs(1)) and - ( - succ = MkAssignNode(recv, 0) - or - not exists(MkAssignNode(recv, 0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(1), pred, normalCompletion()) and - succ = MkExtractNode(recv, 0) - or - ( - pred = MkAssignNode(recv, 0) and - not exists(MkExtractNode(recv, 1)) - or - pred = MkExtractNode(recv, 1) and - not exists(MkAssignNode(recv, 1)) - or - pred = MkAssignNode(recv, 1) - ) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - exists(CommClause cc, SendStmt ss | - cc = this.getCommClause(_) and - ss = cc.getComm() and - pred = MkSendNode(ss) - | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - } - } - - private class SelectorExprTree extends ControlFlowTree, SelectorExpr { - SelectorExprTree() { this.getBase() instanceof ValueExpr } - - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - cmpl = Done() - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | pred = this.getStepWithRank(i) and succ = this.getStepWithRank(i + 1)) - } - - private ControlFlow::Node getStepOrdered(int i) { - i = -2 and lastNode(this.getBase(), result, normalCompletion()) - or - i = -1 and result = MkImplicitDeref(this.getBase()) - or - exists(int maxIndex | - maxIndex = max(int k | k = 0 or exists(MkImplicitFieldSelection(this, k, _))) - | - result = MkImplicitFieldSelection(this, maxIndex - i, _) - or - i = maxIndex and - result = mkExprOrSkipNode(this) - ) - } - - private ControlFlow::Node getStepWithRank(int i) { - exists(int j | - result = this.getStepOrdered(j) and - j = rank[i + 1](int k | exists(this.getStepOrdered(k))) - ) - } - } - - private class SendStmtTree extends ControlFlowTree, SendStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getChannel(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSendNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - not this = any(CommClause cc).getComm() and - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkSendNode(this) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getChannel() - or - i = 1 and result = this.getValue() - } - } - - private class SliceExprTree extends ControlFlowTree, SliceExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = MkExprNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - ) - or - pred = MkImplicitDeref(this.getBase()) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - or - (lastNode(this.getLow(), pred, normalCompletion()) or pred = MkImplicitLowerSliceBound(this)) and - (firstNode(this.getHigh(), succ) or succ = MkImplicitUpperSliceBound(this)) - or - (lastNode(this.getHigh(), pred, normalCompletion()) or pred = MkImplicitUpperSliceBound(this)) and - (firstNode(this.getMax(), succ) or succ = MkImplicitMaxSliceBound(this)) - or - (lastNode(this.getMax(), pred, normalCompletion()) or pred = MkImplicitMaxSliceBound(this)) and - succ = MkExprNode(this) - } - } - - private class StarExprTree extends PostOrderTree, StarExpr { - override ControlFlow::Node getNode() { result = mkExprOrSkipNode(this) } - - override Completion getCompletion() { result = Done() or result = Panic() } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getBase() } - } - - private class SwitchTree extends ControlFlowTree, SwitchStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), first) - or - first = MkImplicitTrue(this) - or - firstNode(this.(TypeSwitchStmt).getTest(), first) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), last, cmpl) - or - lastNode(this.(TypeSwitchStmt).getTest(), last, cmpl) - ) and - ( - not cmpl.isNormal() - or - not exists(this.getDefault()) - ) - or - last = MkImplicitTrue(this) and - cmpl = Bool(true) and - this.getNumCase() = 0 - or - exists(CaseClause cc, int i, Completion inner | - cc = this.getCase(i) and lastNode(cc, last, inner) - | - not exists(this.getDefault()) and - i = this.getNumCase() - 1 and - last = cc.(CaseClauseTree).getExprEnd(cc.getNumExpr() - 1, false) and - inner.isNormal() and - cmpl = inner - or - not last = cc.(CaseClauseTree).getExprEnd(_, _) and - inner.isNormal() and - cmpl = inner - or - if inner = Break(BranchTarget::of(this)) - then cmpl = Done() - else ( - not inner.isNormal() and inner != Fallthrough() and cmpl = inner - ) - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - lastNode(this.getInit(), pred, normalCompletion()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), succ) or - succ = MkImplicitTrue(this) or - firstNode(this.(TypeSwitchStmt).getTest(), succ) - ) - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), pred, normalCompletion()) or - pred = MkImplicitTrue(this) or - lastNode(this.(TypeSwitchStmt).getTest(), pred, normalCompletion()) - ) and - ( - firstNode(this.getNonDefaultCase(0), succ) - or - not exists(this.getANonDefaultCase()) and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i | - cc = this.getNonDefaultCase(i) and - lastNode(cc, pred, normalCompletion()) and - pred = cc.(CaseClauseTree).getExprEnd(_, false) - | - firstNode(this.getNonDefaultCase(i + 1), succ) - or - i = this.getNumNonDefaultCase() - 1 and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i, CaseClause next | - cc = this.getCase(i) and - lastNode(cc, pred, Fallthrough()) and - next = this.getCase(i + 1) - | - firstNode(next.getStmt(0), succ) - or - succ = MkSkipNode(next) - ) - } - } - - private class TypeAssertTree extends PostOrderTree, TypeAssertExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override Completion getCompletion() { - result = Done() - or - // panic due to type mismatch, but not if the assertion appears in an assignment or - // initialization with two variables or a type-switch - not exists(Assignment assgn | assgn.getNumLhs() = 2 and this = assgn.getRhs().stripParens()) and - not exists(ValueSpec vs | vs.getNumName() = 2 and this = vs.getInit().stripParens()) and - not exists(TypeSwitchStmt ts | this = ts.getExpr()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getExpr() } - } - - private class UnaryExprTree extends ControlFlowTree, UnaryExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - last = MkExprNode(this) and - ( - cmpl = Done() - or - this instanceof DerefExpr and cmpl = Panic() - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ(pred, succ) - or - not this = any(RecvStmt recv).getExpr() and - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkExprNode(this) - } - } - - private ControlFlow::Node mkExprOrSkipNode(Expr e) { - result = MkExprNode(e) or - result = MkSkipNode(e) - } - - /** Holds if evaluation of `root` may start at `first`. */ - cached - predicate firstNode(ControlFlowTree root, ControlFlow::Node first) { root.firstNode(first) } - - /** Holds if evaluation of `root` may complete normally after `last`. */ - cached - predicate lastNode(ControlFlowTree root, ControlFlow::Node last) { - lastNode(root, last, normalCompletion()) - } - - private predicate lastNode(ControlFlowTree root, ControlFlow::Node last, Completion cmpl) { - root.lastNode(last, cmpl) - } - - /** Gets a successor of `nd` that is not a `defer` node */ - private ControlFlow::Node notDeferSucc(ControlFlow::Node nd) { - not result = MkDeferNode(_) and - result = succ(nd) - } - - /** Gets `defer` statements that can be the first defer statement after `nd` in the CFG */ - private ControlFlow::Node nextDefer(ControlFlow::Node nd) { - nd = MkDeferNode(_) and - result = MkDeferNode(_) and - ( - result = succ(nd) - or - result = succ(notDeferSucc+(nd)) - ) - } - - /** - * Holds if the function `f` may return without panicking, exiting the process, or looping forever. - * - * This is defined conservatively, and so may also hold of a function that in fact - * cannot return normally, but never fails to hold of a function that can return normally. - */ - cached - predicate mayReturnNormally(ControlFlowTree root) { - exists(ControlFlow::Node last, Completion cmpl | lastNode(root, last, cmpl) and cmpl != Panic()) - } - - /** - * Holds if `pred` is the node for the case `testExpr` in an expression - * switch statement which is switching on `switchExpr`, and `succ` is the - * node to be executed next if the case test succeeds. - */ - cached - predicate isSwitchCaseTestPassingEdge( - ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr - ) { - exists(ExpressionSwitchStmt ess | ess.getExpr() = switchExpr | - ess.getACase().(CaseClauseTree).isPassingEdge(_, pred, succ, testExpr) - ) - } - - /** Gets a successor of `nd`, that is, a node that is executed after `nd`. */ - cached - ControlFlow::Node succ(ControlFlow::Node nd) { any(ControlFlowTree tree).succ(nd, result) } -} diff --git a/ql/lib/semmle/go/controlflow/IR.qll b/ql/lib/semmle/go/controlflow/IR.qll deleted file mode 100644 index 4df0867e4..000000000 --- a/ql/lib/semmle/go/controlflow/IR.qll +++ /dev/null @@ -1,1669 +0,0 @@ -/** - * Provides classes and predicates for working with an intermediate representation (IR) of Go - * programs that is used as the foundation of the control flow and data flow graphs. - * - * In the IR, the program is represented as a set of instructions, which correspond to expressions - * and statements that compute a value or perform an operation (as opposed to providing syntactic - * structure or type information). - * - * Each instruction is also a control-flow node, but there are control-flow nodes that are not - * instructions (synthetic entry and exit nodes, as well as no-op skip nodes). - */ - -import go -private import semmle.go.controlflow.ControlFlowGraphImpl - -/** Provides predicates and classes for working with IR constructs. */ -module IR { - /** - * An IR instruction. - */ - class Instruction extends ControlFlow::Node { - Instruction() { - this instanceof MkExprNode or - this instanceof MkLiteralElementInitNode or - this instanceof MkImplicitLiteralElementIndex or - this instanceof MkAssignNode or - this instanceof MkCompoundAssignRhsNode or - this instanceof MkExtractNode or - this instanceof MkZeroInitNode or - this instanceof MkFuncDeclNode or - this instanceof MkDeferNode or - this instanceof MkGoNode or - this instanceof MkConditionGuardNode or - this instanceof MkIncDecNode or - this instanceof MkIncDecRhs or - this instanceof MkImplicitOne or - this instanceof MkReturnNode or - this instanceof MkResultWriteNode or - this instanceof MkResultReadNode or - this instanceof MkSelectNode or - this instanceof MkSendNode or - this instanceof MkParameterInit or - this instanceof MkArgumentNode or - this instanceof MkResultInit or - this instanceof MkNextNode or - this instanceof MkImplicitTrue or - this instanceof MkCaseCheckNode or - this instanceof MkImplicitLowerSliceBound or - this instanceof MkImplicitUpperSliceBound or - this instanceof MkImplicitMaxSliceBound or - this instanceof MkImplicitDeref or - this instanceof MkImplicitFieldSelection - } - - /** Holds if this instruction reads the value of variable or constant `v`. */ - predicate reads(ValueEntity v) { this.readsField(_, v) or this.readsMethod(_, v) } - - /** Holds if this instruction updates variable or constant `v` to the value of `rhs`. */ - predicate writes(ValueEntity v, Instruction rhs) { this.writesField(_, v, rhs) } - - /** Holds if this instruction reads the value of field `f` on the value of `base`. */ - predicate readsField(Instruction base, Field f) { none() } - - /** Holds if this instruction updates the value of field `f` on the value of `base`. */ - predicate writesField(Instruction base, Field f, Instruction rhs) { none() } - - /** Holds if this instruction looks up method `m` on the value of `receiver`. */ - predicate readsMethod(Instruction receiver, Method m) { none() } - - /** Holds if this instruction reads the value of element `index` on the value of `base`. */ - predicate readsElement(Instruction base, Instruction index) { none() } - - /** Holds if this instruction updates the value of element `index` on the value of `base`. */ - predicate writesElement(Instruction base, Instruction index) { none() } - - /** Gets the type of the result of this instruction, if any. */ - Type getResultType() { none() } - - /** Gets the float value of the result of this instruction, if it can be determined. */ - float getFloatValue() { none() } - - /** Gets the int value of the result of this instruction, if it can be determined. */ - int getIntValue() { none() } - - /** - * Holds if the complex value of the result of this instruction has real part `real` and - * imaginary part `imag`. - */ - predicate hasComplexValue(float real, float imag) { none() } - - /** Gets either `getFloatValue` or `getIntValue` */ - float getNumericValue() { result = this.getFloatValue() or result = this.getIntValue() } - - /** - * Gets the string representation of the exact value of the result of this instruction, - * if any. - * - * For example, for the constant 3.141592653589793238462, this will - * result in 1570796326794896619231/500000000000000000000 - */ - string getExactValue() { none() } - - /** Gets the string value of the result of this instruction, if it can be determined. */ - string getStringValue() { none() } - - /** Gets the Boolean value of the result of this instruction, if it can be determined. */ - boolean getBoolValue() { none() } - - /** Holds if the result of this instruction is known at compile time. */ - predicate isConst() { none() } - - /** - * Holds if the result of this instruction is known at compile time, and is guaranteed not to - * depend on the platform where it is evaluated. - */ - predicate isPlatformIndependentConstant() { none() } - - /** Gets a textual representation of the kind of this instruction. */ - string getInsnKind() { - this instanceof MkExprNode and result = "expression" - or - this instanceof MkLiteralElementInitNode and result = "element init" - or - this instanceof MkImplicitLiteralElementIndex and result = "element index" - or - this instanceof MkAssignNode and result = "assignment" - or - this instanceof MkCompoundAssignRhsNode and result = "right-hand side of compound assignment" - or - this instanceof MkExtractNode and result = "tuple element extraction" - or - this instanceof MkZeroInitNode and result = "zero value" - or - this instanceof MkFuncDeclNode and result = "function declaration" - or - this instanceof MkDeferNode and result = "defer" - or - this instanceof MkGoNode and result = "go" - or - this instanceof MkConditionGuardNode and result = "condition guard" - or - this instanceof MkIncDecNode and result = "increment/decrement" - or - this instanceof MkIncDecRhs and result = "right-hand side of increment/decrement" - or - this instanceof MkImplicitOne and result = "implicit 1" - or - this instanceof MkReturnNode and result = "return" - or - this instanceof MkResultWriteNode and result = "result write" - or - this instanceof MkResultReadNode and result = "result read" - or - this instanceof MkSelectNode and result = "select" - or - this instanceof MkSendNode and result = "send" - or - this instanceof MkParameterInit and result = "parameter initialization" - or - this instanceof MkArgumentNode and result = "argument" - or - this instanceof MkResultInit and result = "result initialization" - or - this instanceof MkNextNode and result = "next key-value pair" - or - this instanceof MkImplicitTrue and result = "implicit true" - or - this instanceof MkCaseCheckNode and result = "case" - or - this instanceof MkImplicitLowerSliceBound and result = "implicit lower bound" - or - this instanceof MkImplicitUpperSliceBound and result = "implicit upper bound" - or - this instanceof MkImplicitMaxSliceBound and result = "implicit maximum" - or - this instanceof MkImplicitDeref and result = "implicit dereference" - or - this instanceof MkImplicitFieldSelection and result = "implicit field selection" - } - } - - /** - * An IR instruction representing the evaluation of an expression. - */ - class EvalInstruction extends Instruction, MkExprNode { - Expr e; - - EvalInstruction() { this = MkExprNode(e) } - - /** Gets the expression underlying this instruction. */ - Expr getExpr() { result = e } - - override predicate reads(ValueEntity v) { e = v.getAReference() } - - override Type getResultType() { result = e.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override float getFloatValue() { result = e.getFloatValue() } - - override int getIntValue() { result = e.getIntValue() } - - override predicate hasComplexValue(float real, float imag) { e.hasComplexValue(real, imag) } - - override string getExactValue() { result = e.getExactValue() } - - override string getStringValue() { result = e.getStringValue() } - - override boolean getBoolValue() { result = e.getBoolValue() } - - override predicate isConst() { e.isConst() } - - override predicate isPlatformIndependentConstant() { e.isPlatformIndependentConstant() } - - override string toString() { result = e.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - e.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An IR instruction that reads the value of a variable, constant, field or array element, - * or refers to a function. - */ - class ReadInstruction extends Instruction { - ReadInstruction() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - (e instanceof ValueName or e instanceof IndexExpr) and - e.(ReferenceExpr).isRvalue() - ) - or - this instanceof ReadResultInstruction - or - this instanceof MkImplicitFieldSelection - } - } - - /** - * Gets the effective base of a selector, index or slice expression, taking implicit dereferences - * and implicit field reads into account. - * - * For a selector expression `b.f`, this could be the implicit dereference `*b`, or the implicit - * field access `b.Embedded` if the field `f` is promoted from an embedded type `Embedded`, or a - * combination of both `*(b.Embedded)`, or simply `b` if neither case applies. - */ - private Instruction selectorBase(Expr e) { - exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1 | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1) and - exists(Expr base | - base = e.(SelectorExpr).getBase() - or - base = e.(IndexExpr).getBase() - or - base = e.(SliceExpr).getBase() - | - result = MkImplicitDeref(base) - or - not exists(MkImplicitDeref(base)) and - result = evalExprInstruction(base) - ) - } - - /** - * An IR instruction that reads a component from a composite object. - * - * This is either a field of a struct, or an element of an array, map, slice or string. - */ - class ComponentReadInstruction extends ReadInstruction { - ComponentReadInstruction() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - e instanceof IndexExpr - or - e.(SelectorExpr).getBase() instanceof ValueExpr and - not e.(SelectorExpr).getSelector() = any(Method method).getAReference() - ) - or - this instanceof MkImplicitFieldSelection - } - - /** Gets the instruction computing the base value on which the field or element is read. */ - Instruction getBase() { - result = this.(ImplicitFieldReadInstruction).getBaseInstruction() - or - result = selectorBase(this.(EvalInstruction).getExpr()) - } - } - - /** - * An IR instruction that reads the value of a field. - * - * On snapshots with incomplete type information, method expressions may sometimes be - * misclassified as field reads. - */ - class FieldReadInstruction extends ComponentReadInstruction { - SelectorExpr e; - int index; - Field field; - - FieldReadInstruction() { - e = this.(EvalInstruction).getExpr() and - index = 0 and - field.getAReference() = e.getSelector() - or - this = MkImplicitFieldSelection(e, index, field) - } - - /** Gets the `SelectorExpr` of this field read. */ - SelectorExpr getSelectorExpr() { result = e } - - /** Gets the index of this field read. */ - int getIndex() { result = index } - - /** Gets the field being read. */ - Field getField() { result = field } - - Instruction getBaseInstruction() { - exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - ) and - ( - result = MkImplicitDeref(e.getBase()) - or - not exists(MkImplicitDeref(e.getBase())) and - result = evalExprInstruction(e.getBase()) - ) - } - - override predicate readsField(Instruction base, Field f) { - base = this.getBaseInstruction() and f = field - } - } - - /** - * An IR instruction for an implicit field read as part of reading a - * promoted field. - * - * If the field that is being implicitly read has a pointer type then this - * instruction represents an implicit dereference of it. - */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } - - override predicate reads(ValueEntity v) { v = field } - - override Type getResultType() { - if field.getType() instanceof PointerType - then result = field.getType().(PointerType).getBaseType() - else result = field.getType() - } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit read of field " + field.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - e.getBase().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An IR instruction that looks up a method. - */ - class MethodReadInstruction extends ReadInstruction, EvalInstruction { - Method method; - override SelectorExpr e; - - MethodReadInstruction() { e.getSelector() = method.getAReference() } - - /** Gets the instruction computing the receiver value on which the method is looked up. */ - Instruction getReceiver() { result = selectorBase(e) } - - /** Gets the method being looked up. */ - Method getMethod() { result = method } - - override predicate readsMethod(Instruction receiver, Method m) { - receiver = this.getReceiver() and m = this.getMethod() - } - } - - /** - * An IR instruction that reads an element of an array, slice, map or string. - */ - class ElementReadInstruction extends ComponentReadInstruction, EvalInstruction { - override IndexExpr e; - - /** Gets the instruction computing the index of the element being looked up. */ - Instruction getIndex() { result = evalExprInstruction(e.getIndex()) } - - override predicate readsElement(Instruction base, Instruction index) { - base = this.getBase() and index = this.getIndex() - } - } - - /** - * An IR instruction that constructs a slice. - */ - class SliceInstruction extends EvalInstruction { - override SliceExpr e; - - /** Gets the instruction computing the base value from which the slice is constructed. */ - Instruction getBase() { result = selectorBase(e) } - - /** Gets the instruction computing the lower bound of the slice. */ - Instruction getLow() { - result = evalExprInstruction(e.getLow()) or - result = implicitLowerSliceBoundInstruction(e) - } - - /** Gets the instruction computing the upper bound of the slice. */ - Instruction getHigh() { - result = evalExprInstruction(e.getHigh()) or - result = implicitUpperSliceBoundInstruction(e) - } - - /** Gets the instruction computing the capacity of the slice. */ - Instruction getMax() { - result = evalExprInstruction(e.getMax()) or - result = implicitMaxSliceBoundInstruction(e) - } - } - - /** - * An IR instruction that writes a memory location. - */ - class WriteInstruction extends Instruction { - WriteTarget lhs; - - WriteInstruction() { - lhs = MkLhs(this, _) - or - lhs = MkLiteralElementTarget(this) - or - lhs = MkResultWriteTarget(this) - } - - /** Gets the target to which this instruction writes. */ - WriteTarget getLhs() { result = lhs } - - /** Gets the instruction computing the value this instruction writes. */ - Instruction getRhs() { none() } - - override predicate writes(ValueEntity v, Instruction rhs) { - this.getLhs().refersTo(v) and - rhs = this.getRhs() - } - } - - /** - * An IR instruction that initializes a component of a composite literal. - */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { - CompositeLit lit; - int i; - Expr elt; - - InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) - } - - /** Gets the instruction allocating the composite literal. */ - Instruction getBase() { result = evalExprInstruction(lit) } - - override Instruction getRhs() { - result = evalExprInstruction(elt) or - result = evalExprInstruction(elt.(KeyValueExpr).getValue()) - } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - elt.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An IR instruction that initializes a field of a struct literal. - */ - class InitLiteralStructFieldInstruction extends InitLiteralComponentInstruction { - override StructLit lit; - - /** Gets the name of the initialized field. */ - string getFieldName() { - if elt instanceof KeyValueExpr - then result = elt.(KeyValueExpr).getKey().(Ident).getName() - else lit.getStructType().hasOwnField(i, result, _, _) - } - - /** Gets the initialized field. */ - Field getField() { - result.getDeclaringType() = lit.getStructType() and - result.getName() = this.getFieldName() - } - } - - /** - * An IR instruction that initializes an element of an array, slice or map literal. - */ - class InitLiteralElementInstruction extends InitLiteralComponentInstruction { - Type literalType; - - InitLiteralElementInstruction() { - literalType = lit.getType().getUnderlyingType() and - ( - literalType instanceof ArrayType or - literalType instanceof SliceType or - literalType instanceof MapType - ) - } - - /** Gets the instruction computing the index of the initialized element. */ - Instruction getIndex() { - result = evalExprInstruction(elt.(KeyValueExpr).getKey()) - or - result = MkImplicitLiteralElementIndex(elt) - } - } - - /** - * An IR instruction that initializes an element of an array literal. - */ - class InitLiteralArrayElementInstruction extends InitLiteralElementInstruction { - override ArrayType literalType; - } - - /** - * An IR instruction that initializes an element of a slice literal. - */ - class InitLiteralSliceElementInstruction extends InitLiteralElementInstruction { - override SliceType literalType; - } - - /** - * An IR instruction that initializes an element of a map literal. - */ - class InitLiteralMapElementInstruction extends InitLiteralElementInstruction { - override MapType literalType; - } - - /** - * An IR instruction that writes to a field. - */ - class FieldWriteInstruction extends WriteInstruction { - override FieldTarget lhs; - - /** Gets the instruction computing the base value on which the field is written. */ - Instruction getBase() { result = lhs.getBase() } - - /** Gets the field being written. */ - Field getField() { result = lhs.getField() } - - override predicate writesField(Instruction base, Field f, Instruction rhs) { - this.getBase() = base and - this.getField() = f and - this.getRhs() = rhs - } - } - - /** - * An IR instruction that writes to an element of an array, slice, or map. - */ - class ElementWriteInstruction extends WriteInstruction { - override ElementTarget lhs; - - /** Gets the instruction computing the base value on which the field is written. */ - Instruction getBase() { result = lhs.getBase() } - - /** Gets the instruction computing the element index being written. */ - Instruction getIndex() { result = lhs.getIndex() } - - override predicate writesElement(Instruction base, Instruction index) { - this.getBase() = base and - this.getIndex() = index - } - } - - /** Holds if `lit` does not specify any explicit keys. */ - private predicate noExplicitKeys(CompositeLit lit) { - not lit.getAnElement() instanceof KeyValueExpr - } - - /** Gets the index of the `i`th element in (array or slice) literal `lit`. */ - private int getElementIndex(CompositeLit lit, int i) { - ( - lit.getType().getUnderlyingType() instanceof ArrayType or - lit.getType().getUnderlyingType() instanceof SliceType - ) and - exists(Expr elt | elt = lit.getElement(i) | - // short-circuit computation for literals without any explicit keys - noExplicitKeys(lit) and result = i - or - result = elt.(KeyValueExpr).getKey().getIntValue() - or - not elt instanceof KeyValueExpr and - ( - i = 0 and result = 0 - or - result = getElementIndex(lit, i - 1) + 1 - ) - ) - } - - /** - * An IR instruction computing the implicit index of an element in an array or slice literal. - */ - class ImplicitLiteralElementIndexInstruction extends Instruction, MkImplicitLiteralElementIndex { - Expr elt; - - ImplicitLiteralElementIndexInstruction() { this = MkImplicitLiteralElementIndex(elt) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override int getIntValue() { - exists(CompositeLit lit, int i | elt = lit.getElement(i) | result = getElementIndex(lit, i)) - } - - override string getStringValue() { none() } - - override string getExactValue() { result = this.getIntValue().toString() } - - override predicate isPlatformIndependentConstant() { any() } - - override predicate isConst() { any() } - - override string toString() { result = "element index" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - elt.hasLocationInfo(filepath, startline, startcolumn, _, _) and - endline = startline and - endcolumn = startcolumn - } - } - - /** - * An instruction assigning to a variable or field. - */ - class AssignInstruction extends WriteInstruction, MkAssignNode { - AstNode assgn; - int i; - - AssignInstruction() { this = MkAssignNode(assgn, i) } - - override Instruction getRhs() { - exists(SimpleAssignStmt a | a = assgn | - a.getNumLhs() = a.getNumRhs() and - result = evalExprInstruction(a.getRhs(i)) - ) - or - exists(ValueSpec spec | spec = assgn | - spec.getNumName() = spec.getNumInit() and - result = evalExprInstruction(spec.getInit(i)) - or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) - ) - or - result = MkCompoundAssignRhsNode(assgn) - or - result = MkExtractNode(assgn, i) - } - - override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = "assignment to " + this.getLhs() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLhs().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** An instruction computing the value of the right-hand side of a compound assignment. */ - class EvalCompoundAssignRhsInstruction extends Instruction, MkCompoundAssignRhsNode { - CompoundAssignStmt assgn; - - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } - - /** Gets the underlying assignment of this instruction. */ - CompoundAssignStmt getAssignment() { result = assgn } - - override Type getResultType() { result = assgn.getRhs().getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = assgn.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - assgn.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction selecting one of multiple values returned by a function, or either the key - * or the value of the iterator in a range loop, or the result or success value from a type - * assertion. - */ - class ExtractTupleElementInstruction extends Instruction, MkExtractNode { - AstNode s; - int i; - - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } - - /** Gets the instruction computing the tuple value from which one value is extracted. */ - Instruction getBase() { - exists(Expr baseExpr | - baseExpr = s.(Assignment).getRhs() or - baseExpr = s.(ValueSpec).getInit() - | - result = evalExprInstruction(baseExpr) - ) - or - result = MkNextNode(s) - or - result = evalExprInstruction(s.(ReturnStmt).getExpr()) - or - result = evalExprInstruction(s.(CallExpr).getArgument(0).stripParens()) - } - - /** Holds if this extracts the `idx`th value of the result of `base`. */ - predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } - - override Type getResultType() { - exists(CallExpr c | this.getBase() = evalExprInstruction(c) | - result = c.getTarget().getResultType(i) - ) - or - exists(Expr e | this.getBase() = evalExprInstruction(e) | - result = e.getType().(TupleType).getComponentType(pragma[only_bind_into](i)) - ) - or - exists(Type rangeType | rangeType = s.(RangeStmt).getDomain().getType().getUnderlyingType() | - exists(Type baseType | - baseType = rangeType.(ArrayType).getElementType() or - baseType = - rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or - baseType = rangeType.(SliceType).getElementType() - | - i = 0 and - result instanceof IntType - or - i = 1 and - result = baseType - ) - or - rangeType instanceof StringType and - ( - i = 0 and - result instanceof IntType - or - result = Builtin::rune().getType() - ) - or - exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() - or - i = 1 and - result = map.getValueType() - ) - or - i = 0 and - result = rangeType.(RecvChanType).getElementType() - or - i = 0 and - result = rangeType.(SendRecvChanType).getElementType() - ) - } - - override ControlFlow::Root getRoot() { result.isRootOf(s) } - - override string toString() { result = s + "[" + i + "]" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - s.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that computes the zero value for a variable or constant. - */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { - ValueEntity v; - - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } - - override Type getResultType() { result = v.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(v.getDeclaration()) } - - override int getIntValue() { - v.getType().getUnderlyingType() instanceof IntegerType and result = 0 - } - - override float getFloatValue() { - v.getType().getUnderlyingType() instanceof FloatType and result = 0.0 - } - - override string getStringValue() { - v.getType().getUnderlyingType() instanceof StringType and result = "" - } - - override boolean getBoolValue() { - v.getType().getUnderlyingType() instanceof BoolType and result = false - } - - override string getExactValue() { - result = this.getIntValue().toString() or - result = this.getFloatValue().toString() or - result = this.getStringValue().toString() or - result = this.getBoolValue().toString() - } - - override predicate isConst() { any() } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "zero value for " + v } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - v.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that corresponds to the declaration of a function. - */ - class DeclareFunctionInstruction extends Instruction, MkFuncDeclNode { - FuncDecl fd; - - DeclareFunctionInstruction() { this = MkFuncDeclNode(fd) } - - override Type getResultType() { result = fd.getType() } - - override string toString() { result = fd.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - fd.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that corresponds to a `defer` statement. - */ - class DeferInstruction extends Instruction, MkDeferNode { - DeferStmt defer; - - DeferInstruction() { this = MkDeferNode(defer) } - - override ControlFlow::Root getRoot() { result.isRootOf(defer) } - - override string toString() { result = defer.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - defer.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that corresponds to a `go` statement. - */ - class GoInstruction extends Instruction, MkGoNode { - GoStmt go; - - GoInstruction() { this = MkGoNode(go) } - - override ControlFlow::Root getRoot() { result.isRootOf(go) } - - override string toString() { result = go.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - go.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that corresponds to an increment or decrement statement. - */ - class IncDecInstruction extends WriteInstruction, MkIncDecNode { - IncDecStmt ids; - - IncDecInstruction() { this = MkIncDecNode(ids) } - - override Instruction getRhs() { result = MkIncDecRhs(ids) } - - override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = ids.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ids.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that computes the (implicit) right-hand side of an increment or - * decrement statement. - */ - class EvalIncDecRhsInstruction extends Instruction, MkIncDecRhs { - IncDecStmt ids; - - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - override Type getResultType() { result = ids.getOperand().getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = "rhs of " + ids } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ids.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction computing the implicit operand `1` in an increment or decrement statement. - */ - class EvalImplicitOneInstruction extends Instruction, MkImplicitOne { - IncDecStmt ids; - - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - override Type getResultType() { result = ids.getOperand().getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override int getIntValue() { result = 1 } - - override string getExactValue() { result = "1" } - - override predicate isConst() { any() } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "1" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ids.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction corresponding to a return from a function. - */ - class ReturnInstruction extends Instruction, MkReturnNode { - ReturnStmt ret; - - ReturnInstruction() { this = MkReturnNode(ret) } - - /** Gets the corresponding `ReturnStmt`. */ - ReturnStmt getReturnStmt() { result = ret } - - /** Holds if this statement returns multiple results. */ - predicate returnsMultipleResults() { exists(MkExtractNode(ret, _)) or ret.getNumExpr() > 1 } - - /** Gets the instruction whose result is the (unique) result returned by this statement. */ - Instruction getResult() { - not this.returnsMultipleResults() and - result = evalExprInstruction(ret.getExpr()) - } - - /** Gets the instruction whose result is the `i`th result returned by this statement. */ - Instruction getResult(int i) { - result = MkExtractNode(ret, i) - or - not exists(MkExtractNode(ret, _)) and - result = evalExprInstruction(ret.getExpr(i)) - } - - override ControlFlow::Root getRoot() { result.isRootOf(ret) } - - override string toString() { result = ret.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ret.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that represents the implicit assignment to a result variable - * performed by a return statement. - */ - class WriteResultInstruction extends WriteInstruction, MkResultWriteNode { - ResultVariable var; - int i; - ReturnInstruction ret; - - WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) - } - - override Instruction getRhs() { result = ret.getResult(i) } - - /** Gets the result variable being assigned. */ - ResultVariable getResultVariable() { result = var } - - override Type getResultType() { result = var.getType() } - - override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit write of " + var } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ret.getResult(i).hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that reads the final value of a result variable upon returning - * from a function. - */ - class ReadResultInstruction extends Instruction, MkResultReadNode { - ResultVariable var; - - ReadResultInstruction() { this = MkResultReadNode(var) } - - override predicate reads(ValueEntity v) { v = var } - - override Type getResultType() { result = var.getType() } - - override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit read of " + var } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - var.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction corresponding to a `select` statement. - */ - class SelectInstruction extends Instruction, MkSelectNode { - SelectStmt sel; - - SelectInstruction() { this = MkSelectNode(sel) } - - override ControlFlow::Root getRoot() { result.isRootOf(sel) } - - override string toString() { result = sel.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - sel.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction corresponding to a send statement. - */ - class SendInstruction extends Instruction, MkSendNode { - SendStmt send; - - SendInstruction() { this = MkSendNode(send) } - - override ControlFlow::Root getRoot() { result.isRootOf(send) } - - override string toString() { result = send.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - send.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction initializing a parameter to the corresponding argument. - */ - class InitParameterInstruction extends WriteInstruction, MkParameterInit { - Parameter parm; - - InitParameterInstruction() { this = MkParameterInit(parm) } - - override Instruction getRhs() { result = MkArgumentNode(parm) } - - override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "initialization of " + parm } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - parm.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction reading the value of a function argument. - */ - class ReadArgumentInstruction extends Instruction, MkArgumentNode { - Parameter parm; - - ReadArgumentInstruction() { this = MkArgumentNode(parm) } - - override Type getResultType() { result = parm.getType() } - - override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "argument corresponding to " + parm } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - parm.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction initializing a result variable to its zero value. - */ - class InitResultInstruction extends WriteInstruction, MkResultInit { - ResultVariable res; - - InitResultInstruction() { this = MkResultInit(res) } - - override Instruction getRhs() { result = MkZeroInitNode(res) } - - override ControlFlow::Root getRoot() { result = res.getFunction() } - - override string toString() { result = "initialization of " + res } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - res.getDeclaration().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction that gets the next key-value pair in a range loop. - */ - class GetNextEntryInstruction extends Instruction, MkNextNode { - RangeStmt rs; - - GetNextEntryInstruction() { this = MkNextNode(rs) } - - /** - * Gets the instruction computing the value whose key-value pairs this instruction reads. - */ - Instruction getDomain() { result = evalExprInstruction(rs.getDomain()) } - - override ControlFlow::Root getRoot() { result.isRootOf(rs) } - - override string toString() { result = "next key-value pair in range" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - rs.getDomain().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction computing the implicit `true` value in an expression-less `switch` statement. - */ - class EvalImplicitTrueInstruction extends Instruction, MkImplicitTrue { - Stmt stmt; - - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } - - override Type getResultType() { result instanceof BoolType } - - override ControlFlow::Root getRoot() { result.isRootOf(stmt) } - - override boolean getBoolValue() { result = true } - - override string getExactValue() { result = "true" } - - override predicate isConst() { any() } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "true" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - stmt.hasLocationInfo(filepath, startline, startcolumn, _, _) and - endline = startline and - endcolumn = startcolumn - } - } - - /** - * An instruction corresponding to the implicit comparison or type check performed by an - * expression in a `case` clause. - * - * For example, consider this `switch` statement: - * - * ```go - * switch x { - * case 2, y+1: - * ... - * } - * ``` - * - * The expressions `2` and `y+1` are implicitly compared to `x`. These comparisons are - * represented by case instructions. - */ - class CaseInstruction extends Instruction, MkCaseCheckNode { - CaseClause cc; - int i; - - CaseInstruction() { this = MkCaseCheckNode(cc, i) } - - override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "case " + cc.getExpr(i) } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - cc.getExpr(i).hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction computing the implicit lower slice bound of zero in a slice expression without - * an explicit lower bound. - */ - class EvalImplicitLowerSliceBoundInstruction extends Instruction, MkImplicitLowerSliceBound { - SliceExpr slice; - - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(slice) } - - override int getIntValue() { result = 0 } - - override string getExactValue() { result = "0" } - - override predicate isConst() { any() } - - override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "0" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - slice.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction computing the implicit upper slice bound in a slice expression without an - * explicit upper bound. - */ - class EvalImplicitUpperSliceBoundInstruction extends Instruction, MkImplicitUpperSliceBound { - SliceExpr slice; - - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } - - override ControlFlow::Root getRoot() { result.isRootOf(slice) } - - override Type getResultType() { result instanceof IntType } - - override string toString() { result = "len" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - slice.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction computing the implicit maximum slice bound in a slice expression without an - * explicit maximum bound. - */ - class EvalImplicitMaxSliceBoundInstruction extends Instruction, MkImplicitMaxSliceBound { - SliceExpr slice; - - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } - - override ControlFlow::Root getRoot() { result.isRootOf(slice) } - - override Type getResultType() { result instanceof IntType } - - override string toString() { result = "cap" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - slice.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An instruction implicitly dereferencing the base in a field or method reference through a - * pointer, or the base in an element or slice reference through a pointer. - */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { - Expr e; - - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } - - /** Gets the operand that is being dereferenced. */ - Expr getOperand() { result = e } - - override Type getResultType() { - result = e.getType().getUnderlyingType().(PointerType).getBaseType() - } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit dereference" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - e.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** A representation of the target of a write instruction. */ - class WriteTarget extends TWriteTarget { - ControlFlow::Node w; - - WriteTarget() { - this = MkLhs(w, _) or this = MkLiteralElementTarget(w) or this = MkResultWriteTarget(w) - } - - /** Gets the write instruction of which this is the target. */ - WriteInstruction getWrite() { result = w } - - /** Gets the name of the variable or field being written to, if any. */ - string getName() { none() } - - /** Gets the SSA variable being written to, if any. */ - SsaVariable asSsaVariable() { - this.getWrite() = result.getDefinition().(SsaExplicitDefinition).getInstruction() - } - - /** Holds if `e` is the variable or field being written to. */ - predicate refersTo(ValueEntity e) { none() } - - /** Gets a textual representation of this target. */ - string toString() { result = "write target" } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - /** A reference to a variable or constant, used as the target of a write. */ - class VarOrConstTarget extends WriteTarget { - Expr loc; - - VarOrConstTarget() { - this = MkLhs(_, loc) and - ( - loc instanceof Ident - or - loc instanceof SelectorExpr and - not loc.(SelectorExpr).getBase() instanceof ReferenceExpr - ) - or - exists(WriteResultInstruction wr | - this = MkResultWriteTarget(wr) and - evalExprInstruction(loc) = wr.getRhs() - ) - } - - override predicate refersTo(ValueEntity e) { - this instanceof MkLhs and - loc = e.getAReference() - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - e = wr.getResultVariable() - ) - } - - override string getName() { - this = MkLhs(_, loc) and - ( - result = loc.(Ident).getName() - or - result = loc.(SelectorExpr).getSelector().getName() - ) - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - result = wr.getResultVariable().getName() - ) - } - - /** Gets the variable this refers to, if any. */ - Variable getVariable() { this.refersTo(result) } - - /** Gets the constant this refers to, if any. */ - Constant getConstant() { this.refersTo(result) } - - override string toString() { result = this.getName() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - loc.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** A reference to a field, used as the target of a write. */ - class FieldTarget extends WriteTarget { - FieldTarget() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.getBase() instanceof ValueExpr) - or - w instanceof InitLiteralStructFieldInstruction - } - - /** Gets the instruction computing the base value on which this field is accessed. */ - Instruction getBase() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | result = selectorBase(sel)) - or - result = w.(InitLiteralStructFieldInstruction).getBase() - } - - /** Get the type of the base of this field access, that is, the type that contains the field. */ - Type getBaseType() { result = this.getBase().getResultType() } - - override predicate refersTo(ValueEntity e) { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.uses(e)) - or - e = w.(InitLiteralStructFieldInstruction).getField() - } - - override string getName() { exists(Field f | this.refersTo(f) | result = f.getName()) } - - /** Gets the field this refers to, if it can be determined. */ - Field getField() { this.refersTo(result) } - - override string toString() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | - result = "field " + sel.getSelector().getName() - ) - or - result = "field " + w.(InitLiteralStructFieldInstruction).getFieldName() - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - exists(SelectorExpr sel | this = MkLhs(_, sel) | - sel.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - ) - or - w.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * A reference to an element of an array, slice or map, used as the target of a write. - */ - class ElementTarget extends WriteTarget { - ElementTarget() { - this = MkLhs(_, any(IndexExpr idx)) - or - w instanceof InitLiteralElementInstruction - } - - /** Gets the instruction computing the base value of this element reference. */ - Instruction getBase() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = selectorBase(idx)) - or - result = w.(InitLiteralComponentInstruction).getBase() - } - - /** Gets the instruction computing the index of this element reference. */ - Instruction getIndex() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = evalExprInstruction(idx.getIndex())) - or - result = w.(InitLiteralElementInstruction).getIndex() - } - - override string toString() { result = "element" } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - exists(IndexExpr idx | this = MkLhs(_, idx) | - idx.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - ) - or - w.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * A pointer dereference, used as the target of a write. - */ - class PointerTarget extends WriteTarget { - Expr lhs; - - PointerTarget() { - this = MkLhs(_, lhs) and - (lhs instanceof StarExpr or lhs instanceof DerefExpr) - } - - /** Gets the instruction computing the pointer value being dereferenced. */ - Instruction getBase() { - exists(Expr base | base = lhs.(StarExpr).getBase() or base = lhs.(DerefExpr).getOperand() | - result = evalExprInstruction(base) - ) - } - - override string toString() { result = lhs.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - lhs.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the (final) instruction computing the value of `e`. - * - * Note that some expressions (such as type expressions or labels) have no corresponding - * instruction, so this predicate is undefined for them. - * - * Short-circuiting expressions that are purely used for control flow (meaning that their - * value is not stored in a variable or used to compute the value of a non-shortcircuiting - * expression) do not have a final instruction either. - */ - Instruction evalExprInstruction(Expr e) { - result = MkExprNode(e) or - result = evalExprInstruction(e.(ParenExpr).getExpr()) - } - - /** - * Gets the instruction corresponding to the initialization of `r`. - */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } - - /** - * Gets the instruction corresponding to the initialization of `p`. - */ - InitParameterInstruction initParamInstruction(Parameter p) { result = MkParameterInit(p) } - - /** - * Gets the instruction corresponding to the `i`th assignment happening at - * `assgn` (0-based). - */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, i) } - - /** - * Gets the instruction corresponding to the `i`th initialization happening - * at `spec` (0-based). - */ - AssignInstruction initInstruction(ValueSpec spec, int i) { result = MkAssignNode(spec, i) } - - /** - * Gets the instruction corresponding to the assignment of the key variable - * of range statement `rs`. - */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } - - /** - * Gets the instruction corresponding to the assignment of the value variable - * of range statement `rs`. - */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } - - /** - * Gets the instruction corresponding to the implicit initialization of `v` - * to its zero value. - */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } - - /** - * Gets the instruction corresponding to the extraction of the `idx`th element - * of the tuple produced by `base`. - */ - ExtractTupleElementInstruction extractTupleElement(Instruction base, int idx) { - result.extractsElement(base, idx) - } - - /** - * Gets the instruction corresponding to the implicit lower bound of slice `e`, if any. - */ - EvalImplicitLowerSliceBoundInstruction implicitLowerSliceBoundInstruction(SliceExpr e) { - result = MkImplicitLowerSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. - */ - EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. - */ - EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) - } - - /** - * Gets the implicit dereference instruction for `e`, where `e` is a pointer used as the base - * in a field/method access, element access, or slice expression. - */ - EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { result = MkImplicitDeref(e) } -} diff --git a/ql/lib/semmle/go/dataflow/DataFlow.qll b/ql/lib/semmle/go/dataflow/DataFlow.qll deleted file mode 100644 index d99ce3bb5..000000000 --- a/ql/lib/semmle/go/dataflow/DataFlow.qll +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Provides a library for local (intra-procedural) and global (inter-procedural) - * data flow analysis: deciding whether data can flow from a _source_ to a - * _sink_. - * - * Unless configured otherwise, _flow_ means that the exact value of - * the source may reach the sink. We do not track flow across pointer - * dereferences or array indexing. To track these types of flow, where the - * exact value may not be preserved, import - * `semmle.code.go.dataflow.TaintTracking`. - * - * To use global (interprocedural) data flow, extend the class - * `DataFlow::Configuration` as documented on that class. To use local - * (intraprocedural) data flow, invoke `DataFlow::localFlow` or - * `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`. - */ - -import go - -/** - * Provides a library for local (intra-procedural) and global (inter-procedural) - * data flow analysis. - */ -module DataFlow { - import semmle.go.dataflow.internal.DataFlowImpl - import Properties -} - -class Read = DataFlow::ReadNode; diff --git a/ql/lib/semmle/go/dataflow/DataFlow2.qll b/ql/lib/semmle/go/dataflow/DataFlow2.qll deleted file mode 100644 index a2bae8bd9..000000000 --- a/ql/lib/semmle/go/dataflow/DataFlow2.qll +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Provides a library for local (intra-procedural) and global (inter-procedural) - * data flow analysis: deciding whether data can flow from a _source_ to a - * _sink_. - * - * Unless configured otherwise, _flow_ means that the exact value of - * the source may reach the sink. We do not track flow across pointer - * dereferences or array indexing. To track these types of flow, where the - * exact value may not be preserved, import - * `semmle.code.go.dataflow.TaintTracking`. - * - * To use global (interprocedural) data flow, extend the class - * `DataFlow::Configuration` as documented on that class. To use local - * (intraprocedural) data flow, invoke `DataFlow::localFlow` or - * `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`. - */ - -import go - -/** - * Provides a library for local (intra-procedural) and global (inter-procedural) - * data flow analysis. - */ -module DataFlow2 { - import semmle.go.dataflow.internal.DataFlowImpl2 - import Properties -} diff --git a/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/ql/lib/semmle/go/dataflow/ExternalFlow.qll deleted file mode 100644 index 1716fa33e..000000000 --- a/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ /dev/null @@ -1,468 +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 package. - * 2. The `type` column selects a type within that package. - * 3. The `subtypes` is a boolean that indicates whether to jump to an - * arbitrary subtype of that type. - * 4. The `name` column optionally selects a specific named member of the type. - * 5. The `signature` column is always empty. - * 6. The `ext` column is always empty. - * 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 "", - * "Argument[n]", or "Argument[n1..n2]": - * - "": 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. - * - "Argument[n1..n2]": Similar to "Argument[n]" but selects any argument - * in the given range. The range is inclusive at both ends. - * - * An `output` can be either "", "Argument[n]", "Argument[n1..n2]", "Parameter", - * "Parameter[n]", "Parameter[n1..n2]", , "ReturnValue", "ReturnValue[n]", or - * "ReturnValue[n1..n2]": - * - "": Selects a read of a selected field, or a selected parameter. - * - "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. - * - "Argument[n1..n2]": Similar to "Argument[n]" but select any argument in - * the given range. The range is inclusive at both ends. - * - "Parameter": Selects the value of a parameter of the selected element. - * "Parameter" is also allowed in case the selected element is already a - * parameter itself. - * - "Parameter[n]": Similar to "Parameter" but restricted to a specific - * numbered parameter (zero-indexed, and `-1` specifies the value of `this`). - * - "Parameter[n1..n2]": Similar to "Parameter[n]" but selects any parameter - * in the given range. The range is inclusive at both ends. - * - "ReturnValue": Selects the first value being returned by the selected - * element. This requires that the selected element is a method with a - * body. - * - "ReturnValue[n]": Similar to "ReturnValue" but selects the specified - * return value. The return values are zero-indexed - * - "ReturnValue[n1..n2]": Similar to "ReturnValue[n]" but selects any - * return value in the given range. The range is inclusive at both ends. - * 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. - */ - -private import go -private import internal.DataFlowPrivate -private import internal.FlowSummaryImpl::Private::External -private import internal.FlowSummaryImplSpecific -private import internal.AccessPathSyntax -private import FlowSummary - -/** - * A module importing the frameworks that provide external flow data, - * ensuring that they are visible to the taint tracking / data flow library. - */ -private module Frameworks { - private import semmle.go.frameworks.Stdlib -} - -private class BuiltinModel extends SummaryModelCsv { - override predicate row(string row) { - row = - [ - ";;false;append;;;Argument[0].ArrayElement;ReturnValue.ArrayElement;value", - ";;false;append;;;Argument[1];ReturnValue.ArrayElement;value" - ] - } -} - -private predicate sourceModelCsv(string row) { none() } - -private predicate sinkModelCsv(string row) { none() } - -private predicate summaryModelCsv(string row) { none() } - -/** - * 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); -} - -private predicate sourceModel(string row) { - sourceModelCsv(row) or - any(SourceModelCsv s).row(row) -} - -private predicate sinkModel(string row) { - sinkModelCsv(row) or - any(SinkModelCsv s).row(row) -} - -private predicate summaryModel(string row) { - summaryModelCsv(row) or - 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 -) { - 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 - ) -} - -/** 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 -) { - 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 - ) -} - -/** 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 -) { - summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, _) -} - -/** Holds if a summary model `row` 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 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 -} - -/** Holds if `package` have CSV framework coverage. */ -private predicate packageHasCsvCoverage(string package) { - sourceModel(package, _, _, _, _, _, _, _) or - sinkModel(package, _, _, _, _, _, _, _) or - summaryModel(package, _, _, _, _, _, _, _, _) -} - -/** - * Holds if `package` and `subpkg` have CSV framework coverage and `subpkg` - * is a subpackage of `package`. - */ -private predicate packageHasASubpackage(string package, string subpkg) { - packageHasCsvCoverage(package) and - packageHasCsvCoverage(subpkg) and - subpkg.prefix(subpkg.indexOf(".")) = package -} - -/** - * Holds if `package` has CSV framework coverage and it is not a subpackage of - * any other package with CSV framework coverage. - */ -private predicate canonicalPackage(string package) { - packageHasCsvCoverage(package) and not packageHasASubpackage(_, package) -} - -/** - * Holds if `package` and `subpkg` have CSV framework coverage, `subpkg` is a - * subpackage of `package` (or they are the same), and `package` is not a - * subpackage of any other package with CSV framework coverage. - */ -private predicate canonicalPackageHasASubpackage(string package, string subpkg) { - canonicalPackage(package) and - (subpkg = package or packageHasASubpackage(package, subpkg)) -} - -/** - * Holds if CSV framework coverage of `package` is `n` api endpoints of the - * kind `(kind, part)`, and `pkgs` is the number of subpackages of `package` - * which have CSV framework coverage (including `package` itself). - */ -predicate modelCoverage(string package, int pkgs, string kind, string part, int n) { - pkgs = strictcount(string subpkg | canonicalPackageHasASubpackage(package, subpkg)) and - ( - part = "source" and - n = - strictcount(string subpkg, string type, boolean subtypes, string name, string signature, - string ext, string output | - canonicalPackageHasASubpackage(package, subpkg) and - sourceModel(subpkg, type, subtypes, name, signature, ext, output, kind) - ) - or - part = "sink" and - n = - strictcount(string subpkg, string type, boolean subtypes, string name, string signature, - string ext, string input | - canonicalPackageHasASubpackage(package, subpkg) and - sinkModel(subpkg, type, subtypes, name, signature, ext, input, kind) - ) - or - part = "summary" and - n = - strictcount(string subpkg, string type, boolean subtypes, string name, string signature, - string ext, string input, string output | - canonicalPackageHasASubpackage(package, subpkg) and - summaryModel(subpkg, type, subtypes, name, signature, ext, input, output, kind) - ) - ) -} - -/** Provides a query predicate to check the CSV data for validation errors. */ -module CsvValidation { - /** Holds if some row in a CSV-based flow model appears to contain typos. */ - query predicate invalidModelRow(string msg) { - 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 - msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model." - or - not type.regexpMatch("[a-zA-Z0-9_\\$<>]*") and - msg = "Dubious type \"" + type + "\" in " + pred + " model." - or - not name.regexpMatch("[a-zA-Z0-9_]*") and - msg = "Dubious name \"" + name + "\" in " + pred + " model." - or - not signature.regexpMatch("|\\([a-zA-Z0-9_\\.\\$<>,\\[\\]]*\\)") and - msg = "Dubious signature \"" + signature + "\" in " + pred + " model." - or - not ext.regexpMatch("|Annotated") and - msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model." - ) - or - 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 parseArg(part, _) - or - part = input.getToken(_) and - parseParam(part, _) - ) and - msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model." - ) - or - 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 = "Parameter" and pred = "source") and - msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model." - ) - or - 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 - msg = - "Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols + - "." - ) - or - exists(string b | - b = row.splitAt(";", 2) and - not b = ["true", "false"] and - msg = "Invalid boolean \"" + b + "\" in " + pred + " model." - ) - ) - } -} - -pragma[nomagic] -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 f, int i) { - i = -1 and result = "(" - or - exists(int n, string p | f.getParameterType(n).toString() = p | - i = 2 * n and result = p - or - i = 2 * n - 1 and result = "," and n != 0 - ) - or - i = 2 * f.getNumParameter() 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. - */ -string paramsString(Function f) { result = concat(int i | | paramsStringPart(f, i) order by i) } - -/** Gets the source/sink/summary element corresponding to the supplied parameters. */ -SourceOrSinkElement interpretElement( - string pkg, string type, boolean subtypes, string name, string signature, string ext -) { - elementSpec(pkg, type, subtypes, name, signature, ext) and - // Go does not need to distinguish functions with signature - signature = "" and - ( - exists(Field f | f.hasQualifiedName(pkg, type, name) | result.asEntity() = f) - or - exists(Method m | m.hasQualifiedName(pkg, type, name) | - result.asEntity() = m - or - subtypes = true and result.asEntity().(Method).implements(m) - ) - or - type = "" and - exists(Entity e | e.hasQualifiedName(pkg, name) | result.asEntity() = e) - ) -} - -/** Holds if there is an external specification for `f`. */ -predicate hasExternalSpecification(Function f) { - f = any(SummarizedCallable sc).asFunction() - or - exists(SourceOrSinkElement e | f = e.asEntity() | sourceElement(e, _, _) or sinkElement(e, _, _)) -} - -private predicate parseField(AccessPathToken c, DataFlow::FieldContent f) { - exists(string fieldRegex, string package, string className, string fieldName | - fieldRegex = "^Field\\[(.*)\\.([^.]+)\\.([^.]+)\\]$" and - package = c.regexpCapture(fieldRegex, 1) and - className = c.regexpCapture(fieldRegex, 2) and - fieldName = c.regexpCapture(fieldRegex, 3) and - f.getField().hasQualifiedName(package, className, fieldName) - ) -} - -/** A string representing a synthetic instance field. */ -class SyntheticField extends string { - SyntheticField() { parseSynthField(_, this) } - - /** - * Gets the type of this field. The default type is `interface{}`, but this can be - * overridden. - */ - Type getType() { result instanceof EmptyInterfaceType } -} - -private predicate parseSynthField(AccessPathToken c, string f) { - c.regexpCapture("SyntheticField\\[([.a-zA-Z0-9]+)\\]", 1) = f -} - -/** Holds if the specification component parses as a `Content`. */ -predicate parseContent(string component, DataFlow::Content content) { - parseField(component, content) - or - parseSynthField(component, content.(DataFlow::SyntheticFieldContent).getField()) - or - component = "ArrayElement" and content instanceof DataFlow::ArrayContent - or - component = "Element" and content instanceof DataFlow::CollectionContent - or - component = "MapKey" and content instanceof DataFlow::MapKeyContent - or - component = "MapValue" and content instanceof DataFlow::MapValueContent -} - -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(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node) - } - - /** - * 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(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node) - } -} - -import Cached diff --git a/ql/lib/semmle/go/dataflow/FlowSummary.qll b/ql/lib/semmle/go/dataflow/FlowSummary.qll deleted file mode 100644 index 39f877471..000000000 --- a/ql/lib/semmle/go/dataflow/FlowSummary.qll +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Provides classes and predicates for definining flow summaries. - */ - -import go -private import internal.FlowSummaryImpl as Impl -private import internal.DataFlowDispatch -private import internal.DataFlowUtil - -// import all instances below -private module Summaries { } - -class SummaryComponent = Impl::Public::SummaryComponent; - -/** Provides predicates for constructing summary components. */ -module SummaryComponent { - import Impl::Public::SummaryComponent - - /** Gets a summary component that represents a qualifier. */ - SummaryComponent qualifier() { result = argument(-1) } - - /** Gets a summary component for field `f`. */ - SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) } - - /** Gets a summary component that represents the return value of a call. */ - SummaryComponent return() { result = return(_) } -} - -class SummaryComponentStack = Impl::Public::SummaryComponentStack; - -/** Provides predicates for constructing stacks of summary components. */ -module SummaryComponentStack { - import Impl::Public::SummaryComponentStack - - /** Gets a singleton stack representing a qualifier. */ - SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) } - - /** Gets a stack representing a field `f` of `object`. */ - SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) { - result = push(SummaryComponent::field(f), object) - } - - /** Gets a singleton stack representing a (normal) return. */ - SummaryComponentStack return() { result = singleton(SummaryComponent::return()) } -} - -class SummarizedCallable = Impl::Public::SummarizedCallable; - -class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; diff --git a/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll b/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll deleted file mode 100644 index c1653f5b3..000000000 --- a/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll +++ /dev/null @@ -1,310 +0,0 @@ -/** - * Provides QL classes for indicating data flow through a function parameter, return value, - * or receiver. - */ - -import go -private import semmle.go.dataflow.internal.DataFlowPrivate - -/** - * An abstract representation of an input to a function, which is either a parameter - * or the receiver parameter. - */ -private newtype TFunctionInput = - TInParameter(int i) { exists(SignatureType s | exists(s.getParameterType(i))) } or - TInReceiver() or - TInResult(int index) { - // the one and only result - index = -1 - or - // one among several results - exists(SignatureType s | exists(s.getResultType(index))) - } - -/** - * An abstract representation of an input to a function, which is either a parameter - * or the receiver parameter. - */ -class FunctionInput extends TFunctionInput { - /** Holds if this represents the `i`th parameter of a function. */ - predicate isParameter(int i) { none() } - - /** Holds if this represents the receiver of a function. */ - predicate isReceiver() { none() } - - /** Holds if this represents the result of a function. */ - predicate isResult() { none() } - - /** Holds if this represents the `i`th result of a function. */ - predicate isResult(int i) { none() } - - /** Gets the data-flow node corresponding to this input for the call `c`. */ - final DataFlow::Node getNode(DataFlow::CallNode c) { result = getEntryNode(c) } - - /** Gets the data-flow node through which data is passed into this input for the call `c`. */ - abstract DataFlow::Node getEntryNode(DataFlow::CallNode c); - - /** Gets the data-flow node through which data from this input enters function `f`. */ - abstract DataFlow::Node getExitNode(FuncDef f); - - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** Defines convenience methods that get particular `FunctionInput` instances. */ -module FunctionInput { - /** Gets a `FunctionInput` representing the `i`th parameter. */ - FunctionInput parameter(int i) { result.isParameter(i) } - - /** Gets a `FunctionInput` representing the receiver. */ - FunctionInput receiver() { result.isReceiver() } - - /** Gets a `FunctionInput` representing the result of a single-result function. */ - FunctionInput functionResult() { result.isResult() } - - /** Gets a `FunctionInput` representing the `i`th result. */ - FunctionInput functionResult(int i) { result.isResult(i) } -} - -/** A parameter position of a function, viewed as a source of input. */ -private class ParameterInput extends FunctionInput, TInParameter { - int index; - - ParameterInput() { this = TInParameter(index) } - - override predicate isParameter(int i) { i = index } - - override DataFlow::Node getEntryNode(DataFlow::CallNode c) { result = c.getArgument(index) } - - override DataFlow::Node getExitNode(FuncDef f) { - result = DataFlow::parameterNode(f.getParameter(index)) - } - - override string toString() { result = "parameter " + index } -} - -/** The receiver of a function, viewed as a source of input. */ -private class ReceiverInput extends FunctionInput, TInReceiver { - override predicate isReceiver() { any() } - - override DataFlow::Node getEntryNode(DataFlow::CallNode c) { - result = c.(DataFlow::MethodCallNode).getReceiver() - } - - override DataFlow::Node getExitNode(FuncDef f) { - result = DataFlow::receiverNode(f.(MethodDecl).getReceiver()) - } - - override string toString() { result = "receiver" } -} - -/** - * A result position of a function, viewed as an input. - * - * Results are usually outputs rather than inputs, but for taint tracking it can be useful to - * think of taint propagating backwards from a result of a function to its arguments. For instance, - * the function `bufio.NewWriter` returns a writer `bw` that buffers write operations to an - * underlying writer `w`. If tainted data is written to `bw`, then it makes sense to propagate - * that taint back to the underlying writer `w`, which can be modeled by saying that - * `bufio.NewWriter` propagates taint from its result to its first argument. - */ -private class ResultInput extends FunctionInput, TInResult { - int index; - - ResultInput() { this = TInResult(index) } - - override predicate isResult() { index = -1 } - - override predicate isResult(int i) { - i = 0 and isResult() - or - i = index and i >= 0 - } - - override DataFlow::Node getEntryNode(DataFlow::CallNode c) { - exists(DataFlow::Node pred | - index = -1 and - pred = c.getResult() - or - index >= 0 and - pred = c.getResult(index) - | - // if the result is assigned to an SSA variable, we want to propagate mutations backwards - // through that variable - exists(DataFlow::SsaNode ssa | ssa.getInit() = pred | result = ssa) - or - // otherwise the entry node is simply the result - not exists(DataFlow::SsaNode ssa | ssa.getInit() = pred) and - result = pred - ) - } - - override DataFlow::Node getExitNode(FuncDef f) { none() } - - override string toString() { - index = -1 and result = "result" - or - index >= 0 and result = "result " + index - } -} - -/** - * An abstract representation of an output of a function, which is one of its results. - */ -private newtype TFunctionOutput = - TOutResult(int index) { - // the one and only result - index = -1 - or - // one among several results - exists(SignatureType s | exists(s.getResultType(index))) - } or - TOutReceiver() or - TOutParameter(int index) { exists(SignatureType s | exists(s.getParameterType(index))) } - -/** - * An abstract representation of an output of a function, which is one of its results - * or a parameter with mutable type. - */ -class FunctionOutput extends TFunctionOutput { - /** Holds if this represents the (single) result of a function. */ - predicate isResult() { none() } - - /** Holds if this represents the `i`th result of a function. */ - predicate isResult(int i) { none() } - - /** Holds if this represents the receiver of a function. */ - predicate isReceiver() { none() } - - /** Holds if this represents the `i`th parameter of a function. */ - predicate isParameter(int i) { none() } - - /** Gets the data-flow node corresponding to this output for the call `c`. */ - final DataFlow::Node getNode(DataFlow::CallNode c) { result = getExitNode(c) } - - /** Gets the data-flow node through which data is passed into this output for the function `f`. */ - abstract DataFlow::Node getEntryNode(FuncDef f); - - /** Gets the data-flow node through which data is returned from this output for the call `c`. */ - abstract DataFlow::Node getExitNode(DataFlow::CallNode c); - - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** Defines convenience methods that get particular `FunctionOutput` instances. */ -module FunctionOutput { - /** Gets a `FunctionOutput` representing the result of a single-result function. */ - FunctionOutput functionResult() { result.isResult() } - - /** Gets a `FunctionOutput` representing the `i`th result. */ - FunctionOutput functionResult(int i) { result.isResult(i) } - - /** Gets a `FunctionOutput` representing the receiver after a function returns. */ - FunctionOutput receiver() { result.isReceiver() } - - /** Gets a `FunctionOutput` representing the `i`th parameter after a function returns. */ - FunctionOutput parameter(int i) { result.isParameter(i) } -} - -/** A result position of a function, viewed as an output. */ -private class OutResult extends FunctionOutput, TOutResult { - int index; - - OutResult() { this = TOutResult(index) } - - override predicate isResult() { index = -1 } - - override predicate isResult(int i) { - i = 0 and isResult() - or - i = index and i >= 0 - } - - override DataFlow::Node getEntryNode(FuncDef f) { - // return expressions - exists(IR::ReturnInstruction ret | f = ret.getRoot() | - index = -1 and - result = DataFlow::instructionNode(ret.getResult()) - or - index >= 0 and - ret.returnsMultipleResults() and - result = DataFlow::instructionNode(ret.getResult(index)) - ) - or - // expressions assigned to result variables - exists(Write w, int nr | nr = f.getType().getNumResult() | - index = -1 and - nr = 1 and - w.writes(f.getResultVar(0), result) - or - index >= 0 and - nr > 1 and - w.writes(f.getResultVar(index), result) - ) - } - - override DataFlow::Node getExitNode(DataFlow::CallNode c) { - index = -1 and result = c.getResult() - or - result = c.getResult(index) - } - - override string toString() { - index = -1 and result = "result" - or - index >= 0 and result = "result " + index - } -} - -/** The receiver of a function, viewed as an output. */ -private class OutReceiver extends FunctionOutput, TOutReceiver { - override predicate isReceiver() { any() } - - override DataFlow::Node getEntryNode(FuncDef f) { - // there is no generic way of assigning to a receiver; operations that taint a receiver - // have to be handled on a case-by-case basis - none() - } - - override DataFlow::Node getExitNode(DataFlow::CallNode c) { - exists(DataFlow::Node arg | - arg = getArgument(c, -1) and - result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg - ) - } - - override string toString() { result = "receiver" } -} - -/** - * A parameter of a function, viewed as an output. - * - * Note that slices passed to varargs parameters using `...` are not included, since in this - * case it is ambiguous whether the output should be the slice itself or one of its elements. - */ -private class OutParameter extends FunctionOutput, TOutParameter { - int index; - - OutParameter() { this = TOutParameter(index) } - - override predicate isParameter(int i) { i = index } - - override DataFlow::Node getEntryNode(FuncDef f) { - // there is no generic way of assigning to a parameter; operations that taint a parameter - // have to be handled on a case-by-case basis - none() - } - - override DataFlow::Node getExitNode(DataFlow::CallNode c) { - exists(DataFlow::Node arg | - arg = getArgument(c, index) and - // exclude slices passed to varargs parameters using `...` calls - not (c.hasEllipsis() and index = c.getNumArgument() - 1) - | - result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg - ) - } - - override string toString() { result = "parameter " + index } -} diff --git a/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll b/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll deleted file mode 100644 index c9c0c58b3..000000000 --- a/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll +++ /dev/null @@ -1,591 +0,0 @@ -/** - * Provides an implementation of Global Value Numbering. - * See https://en.wikipedia.org/wiki/Global_value_numbering - * - * The predicate `globalValueNumber` converts an expression into a `GVN`, - * which is an abstract type representing the value of the expression. If - * two expressions have the same `GVN` then they compute the same value. - * For example: - * - * ``` - * func f(x int, y int) { - * g(x+y, x+y); - * } - * ``` - * - * In this example, both arguments in the call to `g` compute the same value, - * so both arguments have the same `GVN`. In other words, we can find - * this call with the following query: - * - * ``` - * from CallExpr call, GVN v - * where v = globalValueNumber(call.getArgument(0)) - * and v = globalValueNumber(call.getArgument(1)) - * select call - * ``` - * - * The analysis is conservative, so two expressions might have different - * `GVN`s even though the actually always compute the same value. The most - * common reason for this is that the analysis cannot prove that there - * are no side-effects that might cause the computed value to change. - */ - -/* - * Note to developers: the correctness of this module depends on the - * definitions of GVN, globalValueNumber, and analyzableExpr being kept in - * sync with each other. If you change this module then make sure that the - * change is symmetric across all three. - */ - -import go - -/** - * Holds if the result is a control flow node that might change the - * value of any package variable. This is used in the implementation - * of `MkOtherVariable`, because we need to be quite conservative when - * we assign a value number to a package variable. For example: - * - * ``` - * x = g+1; - * dosomething(); - * y = g+1; - * ``` - * - * It is not safe to assign the same value number to both instances - * of `g+1` in this example, because the call to `dosomething` might - * change the value of `g`. - */ -private ControlFlow::Node nodeWithPossibleSideEffect() { - exists(DataFlow::CallNode call | - call.getCall().mayHaveOwnSideEffects() and - not isPureFn(call.getTarget()) and - result = call.asInstruction() - ) - or - // If the lhs of an assignment is not analyzable by SSA, then - // we need to treat the assignment as having a possible side-effect. - result instanceof Write and - not exists(SsaExplicitDefinition ssa | result = ssa.getInstruction()) -} - -private predicate isPureFn(Function f) { - f.(BuiltinFunction).isPure() - or - isPureStmt(f.(DeclaredFunction).getBody()) -} - -private predicate isPureStmt(Stmt s) { - exists(BlockStmt blk | blk = s | forall(Stmt ch | ch = blk.getAStmt() | isPureStmt(ch))) - or - isPureExpr(s.(ReturnStmt).getExpr()) -} - -private predicate isPureExpr(Expr e) { - e instanceof BasicLit - or - exists(FuncDef f | f = e.getEnclosingFunction() | - e = f.getAParameter().getAReference() - or - e = f.(MethodDecl).getReceiver().getAReference() - ) - or - isPureExpr(e.(SelectorExpr).getBase()) - or - exists(CallExpr ce | e = ce | - isPureFn(ce.getTarget()) and - forall(Expr arg | arg = ce.getAnArgument() | isPureExpr(arg)) - ) -} - -/** - * Gets the entry node of the control flow graph of which `node` is a - * member. - */ -private ControlFlow::Node getControlFlowEntry(ControlFlow::Node node) { - result = node.getRoot().getEntryNode() -} - -private predicate entryNode(ControlFlow::Node node) { node.isEntryNode() } - -/** - * Holds if there is a control flow edge from `src` to `dst` or - * if `dst` is an expression with a possible side-effect. The idea - * is to treat side effects as entry points in the control flow - * graph so that we can use the dominator tree to find the most recent - * side-effect. - */ -private predicate sideEffectCFG(ControlFlow::Node src, ControlFlow::Node dst) { - src.getASuccessor() = dst - or - // Add an edge from the entry point to any node that might have a side - // effect. - dst = nodeWithPossibleSideEffect() and - src = getControlFlowEntry(dst) -} - -/** - * Holds if `dominator` is the immediate dominator of `node` in - * the side-effect CFG. - */ -private predicate iDomEffect(ControlFlow::Node dominator, ControlFlow::Node node) = - idominance(entryNode/1, sideEffectCFG/2)(_, dominator, node) - -/** - * Gets the most recent side effect. To be more precise, `result` is a - * dominator of `node` and no side-effects can occur between `result` and - * `node`. - * - * `sideEffectCFG` has an edge from the function entry to every node with a - * side-effect. This means that every node with a side-effect has the - * function entry as its immediate dominator. So if node `x` dominates node - * `y` then there can be no side effects between `x` and `y` unless `x` is - * the function entry. So the optimal choice for `result` has the function - * entry as its immediate dominator. - * - * Example: - * - * ``` - * 000: int f(int a, int b, int *p) { - * 001: int r = 0; - * 002: if (a) { - * 003: if (b) { - * 004: sideEffect1(); - * 005: } - * 006: } else { - * 007: sideEffect2(); - * 008: } - * 009: if (a) { - * 010: r++; // Not a side-effect, because r is an SSA variable. - * 011: } - * 012: if (b) { - * 013: r++; // Not a side-effect, because r is an SSA variable. - * 014: } - * 015: return *p; - * 016: } - * ``` - * - * Suppose we want to find the most recent side-effect for the dereference - * of `p` on line 015. The `sideEffectCFG` has an edge from the function - * entry (line 000) to the side effects at lines 004 and 007. Therefore, - * the immediate dominator tree looks like this: - * - * 000 - 001 - 002 - 003 - * - 004 - * - 007 - * - 009 - 010 - * - 012 - 013 - * - 015 - * - * The immediate dominator path to line 015 is 000 - 009 - 012 - 015. - * Therefore, the most recent side effect for line 015 is line 009. - */ -cached -private ControlFlow::Node mostRecentSideEffect(ControlFlow::Node node) { - exists(ControlFlow::Node entry | - entryNode(entry) and - iDomEffect(entry, result) and - iDomEffect*(result, node) - ) -} - -/** Used to represent the "global value number" of an expression. */ -cached -private newtype GVNBase = - MkNumericConst(string val) { mkNumericConst(_, val) } or - MkStringConst(string val) { mkStringConst(_, val) } or - MkBoolConst(boolean val) { mkBoolConst(_, val) } or - MkIndirectSsa(SsaDefinition def) { not ssaInit(def, _) } or - MkFunc(Function fn) { mkFunc(_, fn) } or - // Variables with no SSA information. As a crude (but safe) - // approximation, we use `mostRecentSideEffect` to compute a definition - // location for the variable. This ensures that two instances of the same - // global variable will only get the same value number if they are - // guaranteed to have the same value. - MkOtherVariable(ValueEntity x, ControlFlow::Node dominator) { mkOtherVariable(_, x, dominator) } or - MkMethodAccess(GVN base, Function m) { mkMethodAccess(_, base, m) } or - MkFieldRead(GVN base, Field f, ControlFlow::Node dominator) { mkFieldRead(_, base, f, dominator) } or - MkPureCall(Function f, GVN callee, GVNList args) { mkPureCall(_, f, callee, args) } or - MkIndex(GVN base, GVN index, ControlFlow::Node dominator) { mkIndex(_, base, index, dominator) } or - // Dereference a pointer. The value might have changed since the last - // time the pointer was dereferenced, so we need to include a definition - // location. As a crude (but safe) approximation, we use - // `mostRecentSideEffect` to compute a definition location. - MkDeref(GVN base, ControlFlow::Node dominator) { mkDeref(_, base, dominator) } or - MkBinaryOp(GVN lhs, GVN rhs, string op) { mkBinaryOp(_, lhs, rhs, op) } or - MkUnaryOp(GVN child, string op) { mkUnaryOp(_, child, op) } or - // Any expression that is not handled by the cases above is - // given a unique number based on the expression itself. - MkUnanalyzable(DataFlow::Node e) { not analyzableExpr(e) } - -private newtype GVNList = - MkNil() or - MkCons(GVN head, GVNList tail) { globalValueNumbers(_, _, head, tail) } - -private GVNList globalValueNumbers(DataFlow::CallNode ce, int start) { - analyzableCall(ce, _) and - start = ce.getNumArgument() and - result = MkNil() - or - exists(GVN head, GVNList tail | - globalValueNumbers(ce, start, head, tail) and - result = MkCons(head, tail) - ) -} - -private predicate globalValueNumbers(DataFlow::CallNode ce, int start, GVN head, GVNList tail) { - analyzableCall(ce, _) and - head = globalValueNumber(ce.getArgument(start)) and - tail = globalValueNumbers(ce, start + 1) -} - -/** - * A Global Value Number. A GVN is an abstract representation of the value - * computed by an expression. The relationship between `Expr` and `GVN` is - * many-to-one: every `Expr` has exactly one `GVN`, but multiple - * expressions can have the same `GVN`. If two expressions have the same - * `GVN`, it means that they compute the same value at run time. The `GVN` - * is an opaque value, so you cannot deduce what the run-time value of an - * expression will be from its `GVN`. The only use for the `GVN` of an - * expression is to find other expressions that compute the same value. - * Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`. - * - * Note: `GVN` has `toString` and `getLocation` methods, so that it can be - * displayed in a results list. These work by picking an arbitrary - * expression with this `GVN` and using its `toString` and `getLocation` - * methods. - */ -class GVN extends GVNBase { - GVN() { this instanceof GVNBase } - - /** Gets a data-flow node that has this GVN. */ - DataFlow::Node getANode() { this = globalValueNumber(result) } - - /** Gets the kind of the GVN. This can be useful for debugging. */ - string getKind() { - this instanceof MkNumericConst and result = "NumericConst" - or - this instanceof MkStringConst and result = "StringConst" - or - this instanceof MkBoolConst and result = "BoolConst" - or - this instanceof MkIndirectSsa and result = "IndirectSsa" - or - this instanceof MkFunc and result = "Func" - or - this instanceof MkOtherVariable and result = "OtherVariable" - or - this instanceof MkMethodAccess and result = "MethodAccess" - or - this instanceof MkFieldRead and result = "FieldRead" - or - this instanceof MkPureCall and result = "PureCall" - or - this instanceof MkIndex and result = "Index" - or - this instanceof MkDeref and result = "Deref" - or - this instanceof MkBinaryOp and result = "BinaryOp" - or - this instanceof MkUnaryOp and result = "UnaryOp" - or - this instanceof MkUnanalyzable and result = "Unanalyzable" - } - - /** - * Gets an example of a data-flow node with this GVN. - * This is useful for things like implementing toString(). - */ - private DataFlow::Node exampleNode() { - // Pick the expression with the minimum source location. This is - // just an arbitrary way to pick an expression with this `GVN`. - result = - min(DataFlow::Node e, string f, int l, int c, string k | - e = getANode() and e.hasLocationInfo(f, l, c, _, _) and k = e.getNodeKind() - | - e order by f, l, c, k - ) - } - - /** Gets a textual representation of this element. */ - string toString() { result = exampleNode().toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - exampleNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate mkNumericConst(DataFlow::Node nd, string val) { - nd.getType().getUnderlyingType() instanceof NumericType and - val = nd.getExactValue() and - nd.isPlatformIndependentConstant() -} - -private predicate mkStringConst(DataFlow::Node nd, string val) { - val = nd.getStringValue() and - nd.isPlatformIndependentConstant() -} - -private predicate mkBoolConst(DataFlow::Node nd, boolean val) { - val = nd.getBoolValue() and - nd.isPlatformIndependentConstant() -} - -private predicate mkFunc(DataFlow::Node nd, Function f) { - nd = f.getARead() and - not f instanceof Method -} - -private predicate analyzableConst(DataFlow::Node e) { - mkNumericConst(e, _) or mkStringConst(e, _) or mkBoolConst(e, _) or mkFunc(e, _) -} - -private predicate analyzableMethodAccess(Read access, DataFlow::Node receiver, Method m) { - exists(IR::ReadInstruction r | r = access.asInstruction() | - r.readsMethod(receiver.asInstruction(), m) and - not r.isConst() - ) -} - -private predicate mkMethodAccess(DataFlow::Node access, GVN qualifier, Method m) { - exists(DataFlow::Node base | - analyzableMethodAccess(access, base, m) and - qualifier = globalValueNumber(base) - ) -} - -private predicate analyzableFieldRead(Read fread, DataFlow::Node base, Field f) { - exists(IR::ReadInstruction r | r = fread.asInstruction() | - r.readsField(base.asInstruction(), f) and - strictcount(mostRecentSideEffect(r)) = 1 and - not r.isConst() - ) -} - -private predicate mkFieldRead( - DataFlow::Node fread, GVN qualifier, Field v, ControlFlow::Node dominator -) { - exists(DataFlow::Node base | - analyzableFieldRead(fread, base, v) and - qualifier = globalValueNumber(base) and - dominator = mostRecentSideEffect(fread.asInstruction()) - ) -} - -private predicate analyzableCall(DataFlow::CallNode ce, Function f) { - f = ce.getTarget() and - isPureFn(f) and - not ce.isConst() -} - -private predicate mkPureCall(DataFlow::CallNode ce, Function f, GVN callee, GVNList args) { - analyzableCall(ce, f) and - callee = globalValueNumber(ce.getCalleeNode()) and - args = globalValueNumbers(ce, 0) -} - -/** - * Holds if `v` is a variable whose value changes are not, or at least not fully, captured by SSA. - * - * This is the case for package variables (for which no SSA information exists), but also for - * variables of non-primitive type (for which deep mutations are not captured by SSA). - */ -private predicate incompleteSsa(ValueEntity v) { - not v instanceof Field and - ( - not v instanceof SsaSourceVariable - or - v.(SsaSourceVariable).mayHaveIndirectReferences() - or - exists(Type tp | tp = v.(DeclaredVariable).getType().getUnderlyingType() | - not tp instanceof BasicType - ) - ) -} - -/** - * Holds if `access` is an access to a variable `target` for which SSA information is incomplete. - */ -private predicate analyzableOtherVariable(DataFlow::Node access, ValueEntity target) { - access.asInstruction().reads(target) and - incompleteSsa(target) and - strictcount(mostRecentSideEffect(access.asInstruction())) = 1 and - not access.isConst() and - not target instanceof Function -} - -private predicate mkOtherVariable(DataFlow::Node access, ValueEntity x, ControlFlow::Node dominator) { - analyzableOtherVariable(access, x) and - dominator = mostRecentSideEffect(access.asInstruction()) -} - -private predicate analyzableBinaryOp( - DataFlow::BinaryOperationNode op, string opname, DataFlow::Node lhs, DataFlow::Node rhs -) { - opname = op.getOperator() and - not op.mayHaveSideEffects() and - lhs = op.getLeftOperand() and - rhs = op.getRightOperand() and - not op.isConst() -} - -private predicate mkBinaryOp(DataFlow::Node op, GVN lhs, GVN rhs, string opname) { - exists(DataFlow::Node l, DataFlow::Node r | - analyzableBinaryOp(op, opname, l, r) and - lhs = globalValueNumber(l) and - rhs = globalValueNumber(r) - ) -} - -private predicate analyzableUnaryOp(DataFlow::UnaryOperationNode op) { - not op.mayHaveSideEffects() and - not op.isConst() -} - -private predicate mkUnaryOp(DataFlow::UnaryOperationNode op, GVN child, string opname) { - analyzableUnaryOp(op) and - child = globalValueNumber(op.getOperand()) and - opname = op.getOperator() -} - -private predicate analyzableIndexExpr(DataFlow::ElementReadNode ae) { - strictcount(mostRecentSideEffect(ae.asInstruction())) = 1 and - not ae.isConst() -} - -private predicate mkIndex( - DataFlow::ElementReadNode ae, GVN base, GVN offset, ControlFlow::Node dominator -) { - analyzableIndexExpr(ae) and - base = globalValueNumber(ae.getBase()) and - offset = globalValueNumber(ae.getIndex()) and - dominator = mostRecentSideEffect(ae.asInstruction()) -} - -private predicate analyzablePointerDereferenceExpr(DataFlow::PointerDereferenceNode deref) { - strictcount(mostRecentSideEffect(deref.asInstruction())) = 1 and - not deref.isConst() -} - -private predicate mkDeref(DataFlow::PointerDereferenceNode deref, GVN p, ControlFlow::Node dominator) { - analyzablePointerDereferenceExpr(deref) and - p = globalValueNumber(deref.getOperand()) and - dominator = mostRecentSideEffect(deref.asInstruction()) -} - -private predicate ssaInit(SsaExplicitDefinition ssa, DataFlow::Node rhs) { - ssa.getRhs() = rhs.asInstruction() -} - -/** Gets the global value number of data-flow node `nd`. */ -cached -GVN globalValueNumber(DataFlow::Node nd) { - exists(string val | - mkNumericConst(nd, val) and - result = MkNumericConst(val) - ) - or - exists(string val | - mkStringConst(nd, val) and - result = MkStringConst(val) - ) - or - exists(boolean val | - mkBoolConst(nd, val) and - result = MkBoolConst(val) - ) - or - exists(Function f | - mkFunc(nd, f) and - result = MkFunc(f) - ) - or - exists(ValueEntity x, ControlFlow::Node dominator | - mkOtherVariable(nd, x, dominator) and - result = MkOtherVariable(x, dominator) - ) - or - exists(GVN qualifier, Function target | - mkMethodAccess(nd, qualifier, target) and - result = MkMethodAccess(qualifier, target) - ) - or - exists(GVN qualifier, Entity target, ControlFlow::Node dominator | - mkFieldRead(nd, qualifier, target, dominator) and - result = MkFieldRead(qualifier, target, dominator) - ) - or - exists(Function f, GVN callee, GVNList args | - mkPureCall(nd, f, callee, args) and - result = MkPureCall(f, callee, args) - ) - or - exists(GVN lhs, GVN rhs, string opname | - mkBinaryOp(nd, lhs, rhs, opname) and - result = MkBinaryOp(lhs, rhs, opname) - ) - or - exists(GVN child, string opname | - mkUnaryOp(nd, child, opname) and - result = MkUnaryOp(child, opname) - ) - or - exists(GVN x, GVN i, ControlFlow::Node dominator | - mkIndex(nd, x, i, dominator) and - result = MkIndex(x, i, dominator) - ) - or - exists(GVN p, ControlFlow::Node dominator | - mkDeref(nd, p, dominator) and - result = MkDeref(p, dominator) - ) - or - not analyzableExpr(nd) and - result = MkUnanalyzable(nd) - or - exists(DataFlow::SsaNode ssa | - nd = ssa.getAUse() and - not incompleteSsa(ssa.getSourceVariable()) and - result = globalValueNumber(ssa) - ) - or - exists(SsaDefinition ssa | ssa = nd.(DataFlow::SsaNode).getDefinition() | - // Local variable with a defining value. - exists(DataFlow::Node init | - ssaInit(ssa, init) and - result = globalValueNumber(init) - ) - or - // Local variable without a defining value. - not ssaInit(ssa, _) and - result = MkIndirectSsa(ssa) - ) -} - -/** - * Holds if the expression is explicitly handled by `globalValueNumber`. - * Unanalyzable expressions still need to be given a global value number, - * but it will be a unique number that is not shared with any other - * expression. - */ -private predicate analyzableExpr(DataFlow::Node e) { - analyzableConst(e) or - any(DataFlow::SsaNode ssa).getAUse() = e or - e instanceof DataFlow::SsaNode or - analyzableOtherVariable(e, _) or - analyzableMethodAccess(e, _, _) or - analyzableFieldRead(e, _, _) or - analyzableCall(e, _) or - analyzableBinaryOp(e, _, _, _) or - analyzableUnaryOp(e) or - analyzableIndexExpr(e) or - analyzablePointerDereferenceExpr(e) -} diff --git a/ql/lib/semmle/go/dataflow/Properties.qll b/ql/lib/semmle/go/dataflow/Properties.qll deleted file mode 100644 index f7df3391f..000000000 --- a/ql/lib/semmle/go/dataflow/Properties.qll +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Provides a class for representing and reasoning about properties of data-flow nodes. - */ - -import go - -private newtype TProperty = - IsBoolean(Boolean b) or - IsNil(Boolean b) - -/** - * A property which may or may not hold of a data-flow node. - * - * Supported properties currently are Boolean truth and `nil`-ness. - */ -class Property extends TProperty { - private predicate checkOnExpr(Expr test, Boolean outcome, DataFlow::Node nd) { - exists(EqualityTestExpr eq, Expr e, boolean isTrue | - eq = test and eq.hasOperands(nd.asExpr(), e) - | - this = IsBoolean(isTrue) and - isTrue = eq.getPolarity().booleanXor(e.getBoolValue().booleanXor(outcome)) - or - this = IsNil(isTrue) and - e = Builtin::nil().getAReference() and - isTrue = eq.getPolarity().booleanXor(outcome).booleanNot() - ) - or - // if test = outcome ==> nd matches this - // then !test = !outcome ==> nd matches this - this.checkOnExpr(test.(NotExpr).getOperand(), outcome.booleanNot(), nd) - or - // if test = outcome ==> nd matches this - // then (test) = outcome ==> nd matches this - this.checkOnExpr(test.(ParenExpr).getExpr(), outcome, nd) - or - // if test = true ==> nd matches this - // then (test && e) = true ==> nd matches this - outcome = true and - this.checkOnExpr(test.(LandExpr).getAnOperand(), outcome, nd) - or - // if test = false ==> nd matches this - // then (test || e) = false ==> nd matches this - outcome = false and - this.checkOnExpr(test.(LorExpr).getAnOperand(), outcome, nd) - or - test = nd.asExpr() and - test instanceof ValueExpr and - test.getType().getUnderlyingType() instanceof BoolType and - this = IsBoolean(outcome) - } - - /** - * Holds if `test` evaluating to `outcome` means that this property holds of `nd`, where `nd` is a - * subexpression of `test`. - */ - predicate checkOn(DataFlow::Node test, Boolean outcome, DataFlow::Node nd) { - this.checkOnExpr(test.asExpr(), outcome, nd) - } - - /** Holds if this is the property of having the Boolean value `b`. */ - predicate isBoolean(boolean b) { this = IsBoolean(b) } - - /** Returns the boolean represented by this property if it is a boolean. */ - boolean asBoolean() { this = IsBoolean(result) } - - /** Holds if this is the property of being `nil`. */ - predicate isNil() { this = IsNil(true) } - - /** Holds if this is the property of being non-`nil`. */ - predicate isNonNil() { this = IsNil(false) } - - /** Gets a textual representation of this property. */ - string toString() { - exists(boolean b | - this = IsBoolean(b) and - result = "is " + b - ) - or - this = IsNil(true) and - result = "is nil" - or - this = IsNil(false) and - result = "is not nil" - } -} - -/** - * Gets a `Property` representing truth outcome `b`. - */ -Property booleanProperty(boolean b) { result = IsBoolean(b) } - -/** - * Gets a `Property` representing `nil`-ness. - */ -Property nilProperty() { result = IsNil(true) } - -/** - * Gets a `Property` representing non-`nil`-ness. - */ -Property notNilProperty() { result = IsNil(false) } diff --git a/ql/lib/semmle/go/dataflow/SSA.qll b/ql/lib/semmle/go/dataflow/SSA.qll deleted file mode 100644 index 713b02e83..000000000 --- a/ql/lib/semmle/go/dataflow/SSA.qll +++ /dev/null @@ -1,411 +0,0 @@ -/** - * Provides classes for working with static single assignment form (SSA). - */ - -import go -private import SsaImpl - -/** - * A variable that can be SSA converted, that is, a local variable, but not a variable - * declared in file scope. - */ -class SsaSourceVariable extends LocalVariable { - SsaSourceVariable() { not this.getScope() instanceof FileScope } - - /** - * Holds if there may be indirect references of this variable that are not covered by `getAReference()`. - * - * This is the case for variables that have their address taken, and for variables whose - * name resolution information may be incomplete (for instance due to an extractor error). - */ - predicate mayHaveIndirectReferences() { - // variables that have their address taken - exists(AddressExpr addr | addr.getOperand().stripParens() = this.getAReference()) - or - exists(DataFlow::MethodReadNode mrn | - mrn.getReceiver() = this.getARead() and - mrn.getMethod().getReceiverType() instanceof PointerType - ) - or - // variables where there is an unresolved reference with the same name in the same - // scope or a nested scope, suggesting that name resolution information may be incomplete - exists(FunctionScope scope, FuncDef inner | - scope = this.getScope().(LocalScope).getEnclosingFunctionScope() and - unresolvedReference(this.getName(), inner) and - inner.getScope().getOuterScope*() = scope - ) - } -} - -/** - * Holds if there is an unresolved reference to `name` in `fn`. - */ -private predicate unresolvedReference(string name, FuncDef fn) { - exists(Ident unresolved | - unresolvedIdentifier(unresolved, name) and - not unresolved = any(SelectorExpr sel).getSelector() and - fn = unresolved.getEnclosingFunction() - ) -} - -/** - * Holds if `id` is an unresolved identifier with the given `name`. - */ -pragma[noinline] -private predicate unresolvedIdentifier(Ident id, string name) { - id.getName() = name and - id instanceof ReferenceExpr and - not id.refersTo(_) -} - -/** - * An SSA variable. - */ -class SsaVariable extends TSsaDefinition { - /** Gets the source variable corresponding to this SSA variable. */ - SsaSourceVariable getSourceVariable() { result = this.(SsaDefinition).getSourceVariable() } - - /** Gets the (unique) definition of this SSA variable. */ - SsaDefinition getDefinition() { result = this } - - /** Gets the type of this SSA variable. */ - Type getType() { result = this.getSourceVariable().getType() } - - /** Gets a use in basic block `bb` that refers to this SSA variable. */ - IR::Instruction getAUseIn(ReachableBasicBlock bb) { - exists(int i, SsaSourceVariable v | v = this.getSourceVariable() | - result = bb.getNode(i) and - this = getDefinition(bb, i, v) - ) - } - - /** Gets a use that refers to this SSA variable. */ - IR::Instruction getAUse() { result = this.getAUseIn(_) } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getDefinition().prettyPrintRef() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getDefinition().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An SSA definition. - */ -class SsaDefinition extends TSsaDefinition { - /** Gets the SSA variable defined by this definition. */ - SsaVariable getVariable() { result = this } - - /** Gets the source variable defined by this definition. */ - abstract SsaSourceVariable getSourceVariable(); - - /** - * Gets the basic block to which this definition belongs. - */ - abstract ReachableBasicBlock getBasicBlock(); - - /** - * INTERNAL: Use `getBasicBlock()` and `getSourceVariable()` instead. - * - * Holds if this is a definition of source variable `v` at index `idx` in basic block `bb`. - * - * Phi nodes are considered to be at index `-1`, all other definitions at the index of - * the control flow node they correspond to. - */ - abstract predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v); - - /** - * INTERNAL: Use `toString()` instead. - * - * Gets a pretty-printed representation of this SSA definition. - */ - abstract string prettyPrintDef(); - - /** - * INTERNAL: Do not use. - * - * Gets a pretty-printed representation of a reference to this SSA definition. - */ - abstract string prettyPrintRef(); - - /** Gets the innermost function or file to which this SSA definition belongs. */ - ControlFlow::Root getRoot() { result = this.getBasicBlock().getRoot() } - - /** Gets a textual representation of this element. */ - string toString() { result = this.prettyPrintDef() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - abstract predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); -} - -/** - * An SSA definition that corresponds to an explicit assignment or other variable definition. - */ -class SsaExplicitDefinition extends SsaDefinition, TExplicitDef { - /** Gets the instruction where the definition happens. */ - IR::Instruction getInstruction() { - exists(BasicBlock bb, int i | this = TExplicitDef(bb, i, _) | result = bb.getNode(i)) - } - - /** Gets the right-hand side of the definition. */ - IR::Instruction getRhs() { this.getInstruction().writes(_, result) } - - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - this = TExplicitDef(bb, i, v) - } - - override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) } - - override SsaSourceVariable getSourceVariable() { this = TExplicitDef(_, _, result) } - - override string prettyPrintRef() { - exists(int l, int c | this.hasLocationInfo(_, l, c, _, _) | result = "def@" + l + ":" + c) - } - - override string prettyPrintDef() { result = "definition of " + this.getSourceVariable() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getInstruction().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** Provides a helper predicate for working with explicit SSA definitions. */ -module SsaExplicitDefinition { - /** - * Gets the SSA definition corresponding to definition `def`. - */ - SsaExplicitDefinition of(IR::Instruction def) { result.getInstruction() = def } -} - -/** - * An SSA definition that does not correspond to an explicit variable definition. - */ -abstract class SsaImplicitDefinition extends SsaDefinition { - /** - * INTERNAL: Do not use. - * - * Gets the definition kind to include in `prettyPrintRef`. - */ - abstract string getKind(); - - override string prettyPrintRef() { - exists(int l, int c | this.hasLocationInfo(_, l, c, _, _) | - result = this.getKind() + "@" + l + ":" + c - ) - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - endline = startline and - endcolumn = startcolumn and - this.getBasicBlock().hasLocationInfo(filepath, startline, startcolumn, _, _) - } -} - -/** - * An SSA definition representing the capturing of an SSA-convertible variable - * in the closure of a nested function. - * - * Capturing definitions appear at the beginning of such functions, as well as - * at any function call that may affect the value of the variable. - */ -class SsaVariableCapture extends SsaImplicitDefinition, TCapture { - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - this = TCapture(bb, i, v) - } - - override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) } - - override SsaSourceVariable getSourceVariable() { this.definesAt(_, _, result) } - - override string getKind() { result = "capture" } - - override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - exists(ReachableBasicBlock bb, int i | this.definesAt(bb, i, _) | - bb.getNode(i).hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - ) - } -} - -/** - * An SSA definition such as a phi node that has no actual semantics, but simply serves to - * merge or filter data flow. - */ -abstract class SsaPseudoDefinition extends SsaImplicitDefinition { - /** - * Gets an input of this pseudo-definition. - */ - abstract SsaVariable getAnInput(); - - /** - * Gets a textual representation of the inputs of this pseudo-definition - * in lexicographical order. - */ - string ppInputs() { result = concat(this.getAnInput().getDefinition().prettyPrintRef(), ", ") } -} - -/** - * An SSA phi node, that is, a pseudo-definition for a variable at a point - * in the flow graph where otherwise two or more definitions for the variable - * would be visible. - */ -class SsaPhiNode extends SsaPseudoDefinition, TPhi { - override SsaVariable getAnInput() { - result = getDefReachingEndOf(this.getBasicBlock().getAPredecessor(), this.getSourceVariable()) - } - - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - bb = this.getBasicBlock() and v = this.getSourceVariable() and i = -1 - } - - override ReachableBasicBlock getBasicBlock() { this = TPhi(result, _) } - - override SsaSourceVariable getSourceVariable() { this = TPhi(_, result) } - - override string getKind() { result = "phi" } - - override string prettyPrintDef() { - result = this.getSourceVariable() + " = phi(" + this.ppInputs() + ")" - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - endline = startline and - endcolumn = startcolumn and - this.getBasicBlock().hasLocationInfo(filepath, startline, startcolumn, _, _) - } -} - -/** - * An SSA variable, possibly with a chain of field reads on it. - */ -private newtype TSsaWithFields = - TRoot(SsaVariable v) or - TStep(SsaWithFields base, Field f) { exists(accessPathAux(base, f)) } - -/** - * Gets a representation of `nd` as an ssa-with-fields value if there is one. - */ -private TSsaWithFields accessPath(IR::Instruction insn) { - exists(SsaVariable v | insn = v.getAUse() | result = TRoot(v)) - or - exists(SsaWithFields base, Field f | insn = accessPathAux(base, f) | result = TStep(base, f)) -} - -/** - * Gets a data-flow node that reads a field `f` from a node that is represented - * by ssa-with-fields value `base`. - */ -private IR::Instruction accessPathAux(TSsaWithFields base, Field f) { - exists(IR::FieldReadInstruction fr, IR::Instruction frb | - fr.getBase() = frb or - fr.getBase() = IR::implicitDerefInstruction(frb.(IR::EvalInstruction).getExpr()) - | - base = accessPath(frb) and - f = fr.getField() and - result = fr - ) -} - -/** An SSA variable with zero or more fields read from it. */ -class SsaWithFields extends TSsaWithFields { - /** - * Gets the SSA variable corresponding to the base of this SSA variable with fields. - * - * For example, the SSA variable corresponding to `a` for the SSA variable with fields - * corresponding to `a.b`. - */ - SsaVariable getBaseVariable() { - this = TRoot(result) - or - exists(SsaWithFields base, Field f | this = TStep(base, f) | result = base.getBaseVariable()) - } - - /** Gets a use that refers to this SSA variable with fields. */ - DataFlow::Node getAUse() { this = accessPath(result.asInstruction()) } - - /** Gets the type of this SSA variable with fields. */ - Type getType() { - exists(SsaVariable var | this = TRoot(var) | result = var.getType()) - or - exists(Field f | this = TStep(_, f) | result = f.getType()) - } - - /** Gets a textual representation of this element. */ - string toString() { - exists(SsaVariable var | this = TRoot(var) | result = "(" + var + ")") - or - exists(SsaWithFields base, Field f | this = TStep(base, f) | result = base + "." + f.getName()) - } - - /** - * Gets an SSA-with-fields variable that is similar to this SSA-with-fields variable in the - * sense that it has the same root variable and the same sequence of field accesses. - */ - SsaWithFields similar() { - result.getBaseVariable().getSourceVariable() = this.getBaseVariable().getSourceVariable() and - result.getQualifiedName() = this.getQualifiedName() - } - - /** - * Gets the qualified name of the source variable or variable and fields that this represents. - * - * For example, for an SSA variable that represents the field `a.b`, this would get the string - * `"a.b"`. - */ - string getQualifiedName() { - exists(SsaVariable v | this = TRoot(v) and result = v.getSourceVariable().getName()) - or - exists(SsaWithFields base, Field f | this = TStep(base, f) | - result = base.getQualifiedName() + "." + f.getName() - ) - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getBaseVariable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * Gets a read similar to `node`, according to the same rules as `SsaWithFields.similar()`. - */ -DataFlow::Node getASimilarReadNode(DataFlow::Node node) { - exists(SsaWithFields readFields | node = readFields.getAUse() | - result = readFields.similar().getAUse() - ) -} diff --git a/ql/lib/semmle/go/dataflow/SsaImpl.qll b/ql/lib/semmle/go/dataflow/SsaImpl.qll deleted file mode 100644 index 0db37ac03..000000000 --- a/ql/lib/semmle/go/dataflow/SsaImpl.qll +++ /dev/null @@ -1,295 +0,0 @@ -/** - * INTERNAL: Analyses should use module `SSA` instead. - * - * Provides predicates for constructing an SSA representation for functions. - */ - -import go - -cached -private module Internal { - /** Holds if the `i`th node of `bb` defines `v`. */ - cached - predicate defAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - bb.getNode(i).(IR::Instruction).writes(v, _) - } - - /** Holds if the `i`th node of `bb` reads `v`. */ - cached - predicate useAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - bb.getNode(i).(IR::Instruction).reads(v) - } - - /** - * A data type representing SSA definitions. - * - * We distinguish three kinds of SSA definitions: - * - * 1. Variable definitions, including declarations, assignments and increments/decrements. - * 2. Pseudo-definitions for captured variables at the beginning of the capturing function - * as well as after calls. - * 3. Phi nodes. - * - * SSA definitions are only introduced where necessary. In particular, - * unreachable code has no SSA definitions associated with it, and neither - * have dead assignments (that is, assignments whose value is never read). - */ - cached - newtype TSsaDefinition = - /** - * An SSA definition that corresponds to an explicit assignment or other variable definition. - */ - TExplicitDef(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - defAt(bb, i, v) and - (liveAfterDef(bb, i, v) or v.isCaptured()) - } or - /** - * An SSA definition representing the capturing of an SSA-convertible variable - * in the closure of a nested function. - * - * Capturing definitions appear at the beginning of such functions, as well as - * at any function call that may affect the value of the variable. - */ - TCapture(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - mayCapture(bb, i, v) and - liveAfterDef(bb, i, v) - } or - /** - * An SSA phi node, that is, a pseudo-definition for a variable at a point - * in the flow graph where otherwise two or more definitions for the variable - * would be visible. - */ - TPhi(ReachableJoinBlock bb, SsaSourceVariable v) { - liveAtEntry(bb, v) and - inDefDominanceFrontier(bb, v) - } - - /** - * Holds if `bb` is in the dominance frontier of a block containing a definition of `v`. - */ - pragma[noinline] - private predicate inDefDominanceFrontier(ReachableJoinBlock bb, SsaSourceVariable v) { - exists(ReachableBasicBlock defbb, SsaDefinition def | - def.definesAt(defbb, _, v) and - bb.inDominanceFrontierOf(defbb) - ) - } - - /** - * Holds if `v` is a captured variable which is declared in `declFun` and read in `useFun`. - */ - private predicate readsCapturedVar(FuncDef useFun, SsaSourceVariable v, FuncDef declFun) { - declFun = v.getDeclaringFunction() and - useFun = any(IR::Instruction u | u.reads(v)).getRoot() and - v.isCaptured() - } - - /** Holds if the `i`th node of `bb` in function `f` is an entry node. */ - private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) { - f = bb.getRoot() and - bb.getNode(i).isEntryNode() - } - - /** - * Holds if the `i`th node of `bb` in function `f` is a function call. - */ - private predicate callNode(FuncDef f, ReachableBasicBlock bb, int i) { - f = bb.getRoot() and - bb.getNode(i).(IR::EvalInstruction).getExpr() instanceof CallExpr - } - - /** - * Holds if the `i`th node of basic block `bb` may induce a pseudo-definition for - * modeling updates to captured variable `v`. Whether the definition is actually - * introduced depends on whether `v` is live at this point in the program. - */ - private predicate mayCapture(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(FuncDef capturingContainer, FuncDef declContainer | - // capture initial value of variable declared in enclosing scope - readsCapturedVar(capturingContainer, v, declContainer) and - capturingContainer != declContainer and - entryNode(capturingContainer, bb, i) - or - // re-capture value of variable after a call if it is assigned non-locally - readsCapturedVar(capturingContainer, v, declContainer) and - assignedThroughClosure(v) and - callNode(capturingContainer, bb, i) - ) - } - - /** A classification of variable references into reads and writes. */ - private newtype RefKind = - ReadRef() or - WriteRef() - - /** - * Holds if the `i`th node of basic block `bb` is a reference to `v`, either a read - * (when `tp` is `ReadRef()`) or a direct or indirect write (when `tp` is `WriteRef()`). - */ - private predicate ref(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind tp) { - useAt(bb, i, v) and tp = ReadRef() - or - (mayCapture(bb, i, v) or defAt(bb, i, v)) and - tp = WriteRef() - } - - /** - * Gets the (1-based) rank of the reference to `v` at the `i`th node of basic block `bb`, - * which has the given reference kind `tp`. - */ - private int refRank(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind tp) { - i = rank[result](int j | ref(bb, j, v, _)) and - ref(bb, i, v, tp) - } - - /** - * Gets the maximum rank among all references to `v` in basic block `bb`. - */ - private int maxRefRank(ReachableBasicBlock bb, SsaSourceVariable v) { - result = max(refRank(bb, _, v, _)) - } - - /** - * Holds if variable `v` is live after the `i`th node of basic block `bb`, where - * `i` is the index of a node that may assign or capture `v`. - * - * For the purposes of this predicate, function calls are considered as writes of captured variables. - */ - private predicate liveAfterDef(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = refRank(bb, i, v, WriteRef()) | - // the next reference to `v` inside `bb` is a read - r + 1 = refRank(bb, _, v, ReadRef()) - or - // this is the last reference to `v` inside `bb`, but `v` is live at entry - // to a successor basic block of `bb` - r = maxRefRank(bb, v) and - liveAtSuccEntry(bb, v) - ) - } - - /** - * Holds if variable `v` is live at the beginning of basic block `bb`. - * - * For the purposes of this predicate, function calls are considered as writes of captured variables. - */ - private predicate liveAtEntry(ReachableBasicBlock bb, SsaSourceVariable v) { - // the first reference to `v` inside `bb` is a read - refRank(bb, _, v, ReadRef()) = 1 - or - // there is no reference to `v` inside `bb`, but `v` is live at entry - // to a successor basic block of `bb` - not exists(refRank(bb, _, v, _)) and - liveAtSuccEntry(bb, v) - } - - /** - * Holds if `v` is live at the beginning of any successor of basic block `bb`. - */ - private predicate liveAtSuccEntry(ReachableBasicBlock bb, SsaSourceVariable v) { - liveAtEntry(bb.getASuccessor(), v) - } - - /** - * Holds if `v` is assigned outside its declaring function. - */ - private predicate assignedThroughClosure(SsaSourceVariable v) { - any(IR::Instruction def | def.writes(v, _)).getRoot() != v.getDeclaringFunction() - } - - /** - * Holds if the `i`th node of `bb` is a use or an SSA definition of variable `v`, with - * `k` indicating whether it is the former or the latter. - */ - private predicate ssaRef(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind k) { - useAt(bb, i, v) and k = ReadRef() - or - any(SsaDefinition def).definesAt(bb, i, v) and k = WriteRef() - } - - /** - * Gets the (1-based) rank of the `i`th node of `bb` among all SSA definitions - * and uses of `v` in `bb`, with `k` indicating whether it is a definition or a use. - * - * For example, if `bb` is a basic block with a phi node for `v` (considered - * to be at index -1), uses `v` at node 2 and defines it at node 5, we have: - * - * ``` - * ssaRefRank(bb, -1, v, WriteRef()) = 1 // phi node - * ssaRefRank(bb, 2, v, ReadRef()) = 2 // use at node 2 - * ssaRefRank(bb, 5, v, WriteRef()) = 3 // definition at node 5 - * ``` - */ - private int ssaRefRank(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind k) { - i = rank[result](int j | ssaRef(bb, j, v, _)) and - ssaRef(bb, i, v, k) - } - - /** - * Gets the minimum rank of a read in `bb` such that all references to `v` between that - * read and the read at index `i` are reads (and not writes). - */ - private int rewindReads(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = ssaRefRank(bb, i, v, ReadRef()) | - exists(int j, RefKind k | r - 1 = ssaRefRank(bb, j, v, k) | - k = ReadRef() and result = rewindReads(bb, j, v) - or - k = WriteRef() and result = r - ) - or - r = 1 and result = r - ) - } - - /** - * Gets the SSA definition of `v` in `bb` that reaches the read of `v` at node `i`, if any. - */ - private SsaDefinition getLocalDefinition(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = rewindReads(bb, i, v) | - exists(int j | result.definesAt(bb, j, v) and ssaRefRank(bb, j, v, _) = r - 1) - ) - } - - /** - * Gets an SSA definition of `v` that reaches the end of the immediate dominator of `bb`. - */ - pragma[noinline] - private SsaDefinition getDefReachingEndOfImmediateDominator( - ReachableBasicBlock bb, SsaSourceVariable v - ) { - result = getDefReachingEndOf(bb.getImmediateDominator(), v) - } - - /** - * Gets an SSA definition of `v` that reaches the end of basic block `bb`. - */ - cached - SsaDefinition getDefReachingEndOf(ReachableBasicBlock bb, SsaSourceVariable v) { - exists(int lastRef | lastRef = max(int i | ssaRef(bb, i, v, _)) | - result = getLocalDefinition(bb, lastRef, v) - or - result.definesAt(bb, lastRef, v) and - liveAtSuccEntry(bb, v) - ) - or - // In SSA form, the (unique) reaching definition of a use is the closest - // definition that dominates the use. If two definitions dominate a node - // then one must dominate the other, so we can find the reaching definition - // by following the idominance relation backwards. - result = getDefReachingEndOfImmediateDominator(bb, v) and - not exists(SsaDefinition ssa | ssa.definesAt(bb, _, v)) and - liveAtSuccEntry(bb, v) - } - - /** - * Gets the unique SSA definition of `v` whose value reaches the `i`th node of `bb`, - * which is a use of `v`. - */ - cached - SsaDefinition getDefinition(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - result = getLocalDefinition(bb, i, v) - or - rewindReads(bb, i, v) = 1 and result = getDefReachingEndOf(bb.getImmediateDominator(), v) - } -} - -import Internal diff --git a/ql/lib/semmle/go/dataflow/TaintTracking.qll b/ql/lib/semmle/go/dataflow/TaintTracking.qll deleted file mode 100644 index 096116d4b..000000000 --- a/ql/lib/semmle/go/dataflow/TaintTracking.qll +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Provides classes for performing local (intra-procedural) and - * global (inter-procedural) taint-tracking analyses. - */ - -import semmle.go.dataflow.DataFlow - -/** - * Provides classes for performing local (intra-procedural) and - * global (inter-procedural) taint-tracking analyses. - */ -module TaintTracking { - import semmle.go.dataflow.internal.tainttracking1.TaintTrackingImpl -} diff --git a/ql/lib/semmle/go/dataflow/TaintTracking2.qll b/ql/lib/semmle/go/dataflow/TaintTracking2.qll deleted file mode 100644 index 6b1b2487e..000000000 --- a/ql/lib/semmle/go/dataflow/TaintTracking2.qll +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Provides classes for performing local (intra-procedural) and - * global (inter-procedural) taint-tracking analyses. - */ - -/** - * Provides classes for performing local (intra-procedural) and - * global (inter-procedural) taint-tracking analyses. - */ -module TaintTracking2 { - import semmle.go.dataflow.internal.tainttracking2.TaintTrackingImpl -} diff --git a/ql/lib/semmle/go/dataflow/barrierguardutil/RedirectCheckBarrierGuard.qll b/ql/lib/semmle/go/dataflow/barrierguardutil/RedirectCheckBarrierGuard.qll deleted file mode 100644 index 6ce0f9014..000000000 --- a/ql/lib/semmle/go/dataflow/barrierguardutil/RedirectCheckBarrierGuard.qll +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs. - */ - -import go - -/** - * A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is - * considered a barrier guard for sanitizing untrusted URLs. - */ -class RedirectCheckBarrierGuard extends DataFlow::BarrierGuard, DataFlow::CallNode { - RedirectCheckBarrierGuard() { - this.getCalleeName().regexpMatch("(?i)(is_?)?(local_?url|valid_?redir(ect)?)(ur[li])?") - } - - override predicate checks(Expr e, boolean outcome) { - // `isLocalUrl(e)` is a barrier for `e` if it evaluates to `true` - this.getAnArgument().asExpr() = e and - outcome = true - } -} diff --git a/ql/lib/semmle/go/dataflow/barrierguardutil/RegexpCheck.qll b/ql/lib/semmle/go/dataflow/barrierguardutil/RegexpCheck.qll deleted file mode 100644 index 64cff45ab..000000000 --- a/ql/lib/semmle/go/dataflow/barrierguardutil/RegexpCheck.qll +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs. - */ - -import go - -/** - * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. - * - * This is overapproximate: we do not attempt to reason about the correctness of the regexp. - * - * Use this if you want to define a derived `DataFlow::BarrierGuard` without - * make the type recursive. Otherwise use `RegexpCheck`. - */ -predicate regexpFunctionChecksExpr(DataFlow::Node resultNode, Expr checked, boolean branch) { - exists(RegexpMatchFunction matchfn, DataFlow::CallNode call | - matchfn.getACall() = call and - resultNode = matchfn.getResult().getNode(call).getASuccessor*() and - checked = matchfn.getValue().getNode(call).asExpr() and - (branch = false or branch = true) - ) -} - -/** - * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. - * - * This is overapproximate: we do not attempt to reason about the correctness of the regexp. - */ -class RegexpCheck extends DataFlow::BarrierGuard { - RegexpCheck() { regexpFunctionChecksExpr(this, _, _) } - - override predicate checks(Expr e, boolean branch) { regexpFunctionChecksExpr(this, e, branch) } -} diff --git a/ql/lib/semmle/go/dataflow/barrierguardutil/UrlCheck.qll b/ql/lib/semmle/go/dataflow/barrierguardutil/UrlCheck.qll deleted file mode 100644 index 8aefc67ee..000000000 --- a/ql/lib/semmle/go/dataflow/barrierguardutil/UrlCheck.qll +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs. - */ - -import go - -/** - * An equality check comparing a data-flow node against a constant string, considered as - * a barrier guard for sanitizing untrusted URLs. - * - * Additionally, a check comparing `url.Hostname()` against a constant string is also - * considered a barrier guard for `url`. - */ -class UrlCheck extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode { - DataFlow::Node url; - - UrlCheck() { - exists(this.getAnOperand().getStringValue()) and - ( - url = this.getAnOperand() - or - exists(DataFlow::MethodCallNode mc | mc = this.getAnOperand() | - mc.getTarget().getName() = "Hostname" and - url = mc.getReceiver() - ) - ) - } - - override predicate checks(Expr e, boolean outcome) { - e = url.asExpr() and outcome = this.getPolarity() - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll b/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll deleted file mode 100644 index ecb6f5429..000000000 --- a/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Module for parsing access paths from CSV models, both the identifying access path used - * by dynamic languages, and the input/output specifications for summary steps. - * - * This file is used by the shared data flow library and by the JavaScript libraries - * (which does not use the shared data flow libraries). - */ - -/** Companion module to the `AccessPath` class. */ -module AccessPath { - /** A string that should be parsed as an access path. */ - abstract class Range extends string { - bindingset[this] - Range() { any() } - } -} - -/** Gets the `n`th token on the access path as a string. */ -private string getRawToken(AccessPath path, int n) { - // Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`. - // Instead use regexpFind to match valid tokens, and supplement with a final length - // check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token. - result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _) -} - -/** - * A string that occurs as an access path (either identifying or input/output spec) - * which might be relevant for this database. - */ -class AccessPath extends string instanceof AccessPath::Range { - /** Holds if this string is not a syntactically valid access path. */ - predicate hasSyntaxError() { - // If the lengths match, all characters must haven been included in a token - // or seen by the `.` lookahead pattern. - this != "" and - not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1 - } - - /** Gets the `n`th token on the access path (if there are no syntax errors). */ - AccessPathToken getToken(int n) { - result = getRawToken(this, n) and - not this.hasSyntaxError() - } - - /** Gets the number of tokens on the path (if there are no syntax errors). */ - int getNumToken() { - result = count(int n | exists(getRawToken(this, n))) and - not this.hasSyntaxError() - } -} - -/** - * An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths. - */ -class AccessPathToken extends string { - AccessPathToken() { this = getRawToken(any(AccessPath path), _) } - - private string getPart(int part) { - result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part) - } - - /** Gets the name of the token, such as `Member` from `Member[x]` */ - string getName() { result = this.getPart(1) } - - /** - * Gets the argument list, such as `1,2` from `Member[1,2]`, - * or has no result if there are no arguments. - */ - string getArgumentList() { result = this.getPart(2) } - - /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */ - string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() } - - /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */ - string getAnArgument() { result = this.getArgument(_) } - - /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */ - int getNumArgument() { result = count(int n | exists(this.getArgument(n))) } -} diff --git a/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll b/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll deleted file mode 100644 index b6c1005da..000000000 --- a/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll +++ /dev/null @@ -1,71 +0,0 @@ -/** Contains predicates for dealing with container flow. */ - -import go -private import DataFlowNodes -private import DataFlowPrivate -private import DataFlowUtil -private import semmle.go.dataflow.ExternalFlow - -/** - * Holds if the step from `node1` to `node2` stores a value in an array, a - * slice, a collection or a map. Thus, `node2` references an object with a - * content `c` that contains the value of `node1`. This covers array - * assignments and initializers as well as implicit array creations for - * varargs. - */ -predicate containerStoreStep(Node node1, Node node2, Content c) { - c instanceof ArrayContent and - ( - ( - node2.getType() instanceof ArrayType or - node2.getType() instanceof SliceType - ) and - exists(Write w | w.writesElement(node2, _, node1)) - ) - or - c instanceof CollectionContent and - exists(SendStmt send | - send.getChannel() = node2.(ExprNode).asExpr() and send.getValue() = node1.(ExprNode).asExpr() - ) - or - c instanceof MapKeyContent and - node2.getType() instanceof MapType and - exists(Write w | w.writesElement(node2, node1, _)) - or - c instanceof MapValueContent and - node2.getType() instanceof MapType and - exists(Write w | w.writesElement(node2, _, node1)) -} - -/** - * Holds if the step from `node1` to `node2` reads a value from an array, a - * slice, a collection or a map. Thus, `node1` references an object with a - * content `c` whose value ends up in `node2`. This covers ordinary array reads - * as well as array iteration through enhanced `for` statements. - */ -predicate containerReadStep(Node node1, Node node2, Content c) { - c instanceof ArrayContent and - ( - node2.(Read).readsElement(node1, _) and - ( - node1.getType() instanceof ArrayType or - node1.getType() instanceof SliceType - ) - or - node2.(RangeElementNode).getBase() = node1 - ) - or - c instanceof CollectionContent and - exists(UnaryOperationNode recv | recv = node2 | - node1 = recv.getOperand() and - recv.getOperator() = "<-" - ) - or - c instanceof MapKeyContent and - node1.getType() instanceof MapType and - node2.(RangeIndexNode).getBase() = node1 - or - c instanceof MapValueContent and - node1.getType() instanceof MapType and - node2.(Read).readsElement(node1, _) -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll deleted file mode 100644 index 019d2831e..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll +++ /dev/null @@ -1,110 +0,0 @@ -private import go -private import DataFlowPrivate - -/** - * Holds if `call` is an interface call to method `m`, meaning that its receiver `recv` has - * interface type `tp`. - */ -private predicate isInterfaceCallReceiver( - DataFlow::CallNode call, DataFlow::Node recv, InterfaceType tp, string m -) { - call.getReceiver() = recv and - recv.getType().getUnderlyingType() = tp and - m = call.getACalleeIncludingExternals().asFunction().getName() -} - -/** Gets a data-flow node that may flow into the receiver value of `call`, which is an interface value. */ -private DataFlow::Node getInterfaceCallReceiverSource(DataFlow::CallNode call) { - exists(DataFlow::Node succ | basicLocalFlowStep*(result, succ) | - isInterfaceCallReceiver(call, succ, _, _) - ) -} - -/** Gets the type of `nd`, which must be a valid type and not an interface type. */ -private Type getConcreteType(DataFlow::Node nd) { - result = nd.getType() and - not result.getUnderlyingType() instanceof InterfaceType and - not result instanceof InvalidType -} - -/** - * Holds if all concrete (that is, non-interface) types of `nd` concrete types can be determined by - * local reasoning. - * - * `nd` is restricted to nodes that flow into the receiver value of an interface call, since that is - * all we are ultimately interested in. - */ -private predicate isConcreteValue(DataFlow::Node nd) { - nd = getInterfaceCallReceiverSource(_) and - ( - exists(getConcreteType(nd)) - or - forex(DataFlow::Node pred | basicLocalFlowStep(pred, nd) | isConcreteValue(pred)) - ) -} - -/** - * Holds if `call` is an interface call to method `m` with receiver `recv`, where the concrete - * types of `recv` can be established by local reasoning. - */ -private predicate isConcreteInterfaceCall(DataFlow::Node call, DataFlow::Node recv, string m) { - isInterfaceCallReceiver(call, recv, _, m) and isConcreteValue(recv) -} - -/** - * Gets a function that might be called by `call`, where the receiver of `call` has interface type, - * but its concrete types can be determined by local reasoning. - */ -private DataFlowCallable getConcreteTarget(DataFlow::CallNode call) { - exists(DataFlow::Node recv, string m | isConcreteInterfaceCall(call, recv, m) | - exists(Type concreteReceiverType | - concreteReceiverType = getConcreteType(getInterfaceCallReceiverSource(call)) and - result.asFunction() = concreteReceiverType.getMethod(m) - ) - ) -} - -/** - * Holds if `call` is a method call whose receiver has an interface type. - */ -private predicate isInterfaceMethodCall(DataFlow::CallNode call) { - isInterfaceCallReceiver(call, _, _, _) -} - -/** - * Gets a method that might be called by `call`, where we restrict the result to - * implement the interface type of the receiver of `call`. - */ -private DataFlowCallable getRestrictedInterfaceTarget(DataFlow::CallNode call) { - exists(InterfaceType tp, Type recvtp, string m | - isInterfaceCallReceiver(call, _, tp, m) and - result.asFunction() = recvtp.getMethod(m) and - recvtp.implements(tp) - ) -} - -/** - * Gets a function that might be called by `call`. - */ -DataFlowCallable viableCallable(CallExpr ma) { - exists(DataFlow::CallNode call | call.asExpr() = ma | - if isConcreteInterfaceCall(call, _, _) - then result = getConcreteTarget(call) - else - if isInterfaceMethodCall(call) - then result = getRestrictedInterfaceTarget(call) - else result.asCallable() = call.getACalleeIncludingExternals() - ) -} - -/** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ -predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { none() } - -/** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ -DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll deleted file mode 100644 index f535bef32..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll +++ /dev/null @@ -1,4697 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Configuration` class. This file exists in several identical - * copies, allowing queries to use multiple `Configuration` classes that depend - * on each other without introducing mutual recursion among those configurations. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic - -/** - * A configuration of interprocedural data flow analysis. This defines - * sources, sinks, and any other configurable aspect of the analysis. Each - * use of the global data flow library must define its own unique extension - * of this abstract class. To create a configuration, extend this class with - * a subclass whose characteristic predicate is a unique singleton string. - * For example, write - * - * ```ql - * class MyAnalysisConfiguration extends DataFlow::Configuration { - * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } - * // Override `isSource` and `isSink`. - * // Optionally override `isBarrier`. - * // Optionally override `isAdditionalFlowStep`. - * } - * ``` - * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and - * the edges are those data-flow steps that preserve the value of the node - * along with any additional edges defined by `isAdditionalFlowStep`. - * Specifying nodes in `isBarrier` will remove those nodes from the graph, and - * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going - * and/or out-going edges from those nodes, respectively. - * - * Then, to query whether there is flow between some `source` and `sink`, - * write - * - * ```ql - * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) - * ``` - * - * Multiple configurations can coexist, but two classes extending - * `DataFlow::Configuration` should never depend on each other. One of them - * should instead depend on a `DataFlow2::Configuration`, a - * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. - */ -abstract class Configuration extends string { - bindingset[this] - Configuration() { any() } - - /** - * Holds if `source` is a relevant data flow source. - */ - abstract predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - abstract predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node) { none() } - - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ - predicate isBarrierGuard(BarrierGuard guard) { none() } - - /** - * Holds if the additional flow step from `node1` to `node2` must be taken - * into account in the analysis. - */ - predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, Content c) { none() } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - */ - FlowFeature getAFeature() { none() } - - /** - * Holds if data may flow from `source` to `sink` for this configuration. - */ - predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } - - /** - * Holds if data may flow from `source` to `sink` for this configuration. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } - - /** - * Holds if data may flow from some source to `sink` for this configuration. - */ - predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } - - /** - * Holds if data may flow from some source to `sink` for this configuration. - */ - predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } - - /** - * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` - * measured in approximate number of interprocedural steps. - */ - int explorationLimit() { none() } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * This predicate is disabled (has no results) by default. Override - * `explorationLimit()` with a suitable number to enable this predicate. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node, this) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * This predicate is disabled (has no results) by default. Override - * `explorationLimit()` with a suitable number to enable this predicate. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink, this) and - dist = node.getSinkDistance() - } -} - -/** - * This class exists to prevent mutual recursion between the user-overridden - * member predicates of `Configuration` and the rest of the data-flow library. - * Good performance cannot be guaranteed in the presence of such recursion, so - * it should be replaced by using more than one copy of the data flow library. - */ -abstract private class ConfigurationRecursionPrevention extends Configuration { - bindingset[this] - ConfigurationRecursionPrevention() { any() } - - override predicate hasFlow(Node source, Node sink) { - strictcount(Node n | this.isSource(n)) < 0 - or - strictcount(Node n | this.isSink(n)) < 0 - or - strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 - or - super.hasFlow(source, sink) - } -} - -private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] - } - -private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } -} - -private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, int i) { - this.asNode().(ParamNode).isParameterOf(c, i) - } - - int getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } -} - -private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } -} - -private predicate inBarrier(NodeEx node, Configuration config) { - exists(Node n | - node.asNode() = n and - config.isBarrierIn(n) and - config.isSource(n) - ) -} - -private predicate outBarrier(NodeEx node, Configuration config) { - exists(Node n | - node.asNode() = n and - config.isBarrierOut(n) and - config.isSink(n) - ) -} - -pragma[nomagic] -private predicate fullBarrier(NodeEx node, Configuration config) { - exists(Node n | node.asNode() = n | - config.isBarrier(n) - or - config.isBarrierIn(n) and - not config.isSource(n) - or - config.isBarrierOut(n) and - not config.isSink(n) - or - exists(BarrierGuard g | - config.isBarrierGuard(g) and - n = g.getAGuardedNode() - ) - ) -} - -pragma[nomagic] -private predicate sourceNode(NodeEx node, Configuration config) { - config.isSource(node.asNode()) and - not fullBarrier(node, config) -} - -pragma[nomagic] -private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) } - -/** Provides the relevant barriers for a step from `node1` to `node2`. */ -pragma[inline] -private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { - not outBarrier(node1, config) and - not inBarrier(node2, config) and - not fullBarrier(node1, config) and - not fullBarrier(node2, config) -} - -/** - * Holds if data can flow in one local step from `node1` to `node2`. - */ -private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2, config) - ) - or - exists(Node n | - config.allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1, config) - ) -} - -/** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ -private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - pragma[only_bind_into](config) - .isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2, config) - ) - or - exists(Node n | - config.allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2, config) - ) -} - -/** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ -private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2, config) and - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) -} - -/** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ -private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - pragma[only_bind_into](config) - .isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2, config) and - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) -} - -private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - read(pragma[only_bind_into](n1), c, pragma[only_bind_into](n2)) and - stepFilter(node1, node2, config) - ) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - config.allowImplicitRead(n, c) - ) -} - -private predicate store( - NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config -) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - store(pragma[only_bind_into](n1), tc, pragma[only_bind_into](n2), contentType) and - read(_, tc.getContent(), _, config) and - stepFilter(node1, node2, config) - ) -} - -pragma[nomagic] -private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) -} - -pragma[nomagic] -private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) -} - -/** - * Holds if field flow should be used for the given configuration. - */ -private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } - -private predicate hasSourceCallCtx(Configuration config) { - exists(FlowFeature feature | feature = config.getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) -} - -private predicate hasSinkCallCtx(Configuration config) { - exists(FlowFeature feature | feature = config.getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) -} - -private module Stage1 { - class ApApprox = Unit; - - class Ap = Unit; - - class ApOption = Unit; - - class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source in the configuration `config`. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { - sourceNode(node, config) and - if hasSourceCallCtx(config) then cc = true else cc = false - or - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - localFlowStep(mid, node, config) - ) - or - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - additionalLocalFlowStep(mid, node, config) - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, config) and - jumpStep(mid, node, config) and - cc = false - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, config) and - additionalJumpStep(mid, node, config) and - cc = false - ) - or - // store - exists(NodeEx mid | - useFieldFlow(config) and - fwdFlow(mid, cc, config) and - store(mid, _, node, _, config) - ) - or - // read - exists(Content c | - fwdFlowRead(c, node, cc, config) and - fwdFlowConsCand(c, config) - ) - or - // flow into a callable - exists(NodeEx arg | - fwdFlow(arg, _, config) and - viableParamArgEx(_, node, arg) and - cc = true and - not fullBarrier(node, config) - ) - or - // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, false, config) and - cc = false - or - fwdFlowOutFromArg(call, node, config) and - fwdFlowIsEntered(call, cc, config) - ) - } - - private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } - - pragma[nomagic] - private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) { - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - read(mid, c, node, config) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c, Configuration config) { - exists(NodeEx mid, NodeEx node, TypedContent tc | - not fullBarrier(node, config) and - useFieldFlow(config) and - fwdFlow(mid, _, config) and - store(mid, tc, node, _, config) and - c = tc.getContent() - ) - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { - exists(RetNodeEx ret | - fwdFlow(ret, cc, config) and - ret.getReturnPosition() = pos - ) - } - - pragma[nomagic] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc, config) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { - fwdFlowOut(call, out, true, config) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgNodeEx arg | - fwdFlow(arg, cc, config) and - viableParamArgEx(call, _, arg) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink in the - * configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { - revFlow0(node, toReturn, config) and - fwdFlow(node, config) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { - fwdFlow(node, config) and - sinkNode(node, config) and - if hasSinkCallCtx(config) then toReturn = true else toReturn = false - or - exists(NodeEx mid | - localFlowStep(node, mid, config) and - revFlow(mid, toReturn, config) - ) - or - exists(NodeEx mid | - additionalLocalFlowStep(node, mid, config) and - revFlow(mid, toReturn, config) - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, config) and - toReturn = false - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid, config) and - revFlow(mid, _, config) and - toReturn = false - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn, config) and - revFlowConsCand(c, config) - ) - or - // read - exists(NodeEx mid, Content c | - read(node, c, mid, config) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - revFlow(mid, toReturn, pragma[only_bind_into](config)) - ) - or - // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, false, config) and - toReturn = false - or - revFlowInToReturn(call, node, config) and - revFlowIsReturned(call, toReturn, config) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos, config) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c, Configuration config) { - exists(NodeEx mid, NodeEx node | - fwdFlow(node, pragma[only_bind_into](config)) and - read(node, c, mid, config) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { - exists(NodeEx mid, TypedContent tc | - revFlow(mid, toReturn, pragma[only_bind_into](config)) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - store(node, tc, mid, _, config) and - c = tc.getContent() - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { - revFlowConsCand(c, conf) and - revFlowStore(c, _, _, conf) - } - - pragma[nomagic] - predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config - ) { - fwdFlowReturnPosition(pos, _, config) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos, Configuration config) { - exists(DataFlowCall call, NodeEx out | - revFlow(out, _, config) and - viableReturnPosOutNodeCandFwd1(call, pos, out, config) - ) - } - - pragma[nomagic] - predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config - ) { - viableParamArgEx(call, p, arg) and - fwdFlow(arg, config) - } - - pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config - ) { - exists(ParamNodeEx p | - revFlow(p, toReturn, config) and - viableParamArgNodeCandFwd1(call, p, arg, config) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { - revFlowIn(call, arg, true, config) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { - exists(NodeEx out | - revFlow(out, toReturn, config) and - fwdFlowOutFromArg(call, out, config) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Content c | - revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and - revFlow(node2, pragma[only_bind_into](config)) and - store(node1, tc, node2, contentType, config) and - c = tc.getContent() and - exists(ap1) - ) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and - revFlow(n2, pragma[only_bind_into](config)) and - read(n1, c, n2, pragma[only_bind_into](config)) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } - - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow(node, toReturn, config) and exists(returnAp) and exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node, Configuration config) { - revFlow(node, true, config) and - fwdFlow(node, true, config) and - not inBarrier(node, config) and - not outBarrier(node, config) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand( - DataFlowCallable callable, ReturnKindExt kind, Configuration config - ) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret, config) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | - throughFlowNodeCand(p, config) and - returnFlowCallableNodeCand(c, kind, config) and - p.getEnclosingCallable() = c and - exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn, config) and - revFlowInToReturn(call, arg, config) and - revFlowIsReturned(call, toReturn, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, config)) and - fields = count(Content f0 | fwdFlowConsCand(f0, config)) and - conscand = -1 and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, config)) and - fields = count(Content f0 | revFlowConsCand(f0, config)) and - conscand = -1 and - tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) - } - /* End: Stage 1 logic. */ -} - -pragma[noinline] -private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { - Stage1::revFlow(node2, config) and - localFlowStep(node1, node2, config) -} - -pragma[noinline] -private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { - Stage1::revFlow(node2, config) and - additionalLocalFlowStep(node1, node2, config) -} - -pragma[nomagic] -private predicate viableReturnPosOutNodeCand1( - DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config -) { - Stage1::revFlow(out, config) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) -} - -/** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ -pragma[nomagic] -private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config -) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) -} - -pragma[nomagic] -private predicate viableParamArgNodeCand1( - DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config -) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and - Stage1::revFlow(arg, config) -} - -/** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ -pragma[nomagic] -private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config -) { - viableParamArgNodeCand1(call, p, arg, config) and - Stage1::revFlow(p, config) and - not outBarrier(arg, config) and - not inBarrier(p, config) -} - -/** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ -private int branch(NodeEx n1, Configuration conf) { - result = - strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - ) -} - -/** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ -private int join(NodeEx n2, Configuration conf) { - result = - strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - ) -} - -/** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ -pragma[nomagic] -private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config -) { - flowOutOfCallNodeCand1(call, ret, out, config) and - exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) -} - -/** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ -pragma[nomagic] -private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config -) { - flowIntoCallNodeCand1(call, arg, p, config) and - exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) -} - -private module Stage2 { - module PrevStage = Stage1; - - class ApApprox = PrevStage::Ap; - - class Ap = boolean; - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } - - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } - - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - class Cc = CallContext; - - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2, config) - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2, config) - ) and - exists(ap) and - exists(lcc) - } - - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - private predicate flowIntoCall = flowIntoCallNodeCand1/5; - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { any() } - - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 2 logic. */ -} - -pragma[nomagic] -private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config -) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) and - Stage2::revFlow(node1, pragma[only_bind_into](config)) -} - -pragma[nomagic] -private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, - Configuration config -) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) and - Stage2::revFlow(node1, pragma[only_bind_into](config)) -} - -private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - predicate localFlowEntry(NodeEx node, Configuration config) { - Stage2::revFlow(node, config) and - ( - sourceNode(node, config) or - jumpStep(_, node, config) or - additionalJumpStep(_, node, config) or - node instanceof ParamNodeEx or - node.asNode() instanceof OutNodeExt or - store(_, _, node, _, config) or - read(_, _, node, config) or - node instanceof FlowCheckNode - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, Configuration config) { - exists(NodeEx next | Stage2::revFlow(next, config) | - jumpStep(node, next, config) or - additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next, _, config) or - read(node, _, next, config) - ) - or - node instanceof FlowCheckNode - or - sinkNode(node, config) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, NodeEx node2, Configuration config - ) { - additionalLocalFlowStepNodeCand1(node1, node2, config) and - Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config)) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config, - LocalCallContext cc - ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and - ( - localFlowEntry(node1, pragma[only_bind_into](config)) and - ( - localFlowStepNodeCand1(node1, node2, config) and - preservesValue = true and - t = node1.getDataFlowType() // irrelevant dummy value - or - additionalLocalFlowStepNodeCand2(node1, node2, config) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - or - exists(NodeEx mid | - localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and - localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and - additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf, - Configuration config, LocalCallContext callContext - ) { - localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and - localFlowExit(node2, config) - } -} - -private import LocalFlowBigStep - -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - private ApNil getApNil(NodeEx node) { - PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) - } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } - - pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - class Cc = boolean; - - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc) - } - - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - private predicate flowIntoCall = flowIntoCallNodeCand2/5; - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) } - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { - not clear(node, ap) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() - } - - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(ap.getType(), contentType) - } - - /* Begin: Stage 3 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 3 logic. */ -} - -/** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ -private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) { - exists(AccessPathFront apf | - Stage3::revFlow(node, true, _, apf, config) and - Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) - ) -} - -/** - * Holds if a length 2 access path approximation with the head `tc` is expected - * to be expensive. - */ -private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and - nodes = - strictcount(NodeEx n | - Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) - or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not tc.forceHighPrecision() - ) -} - -private newtype TAccessPathApprox = - TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { - Stage3::consCand(tc, TFrontNil(t), _) and - not expensiveLen2unfolding(tc, _) - } or - TConsCons(TypedContent tc1, TypedContent tc2, int len) { - Stage3::consCand(tc1, TFrontHead(tc2), _) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(tc1, _) - } or - TCons1(TypedContent tc, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(tc, _) - } - -/** - * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only - * the first two elements of the list and its length are tracked. If data flows - * from a source to a given node with a given `AccessPathApprox`, this indicates - * the sequence of dereference operations needed to get from the value in the node - * to the tracked object. The final type indicates the type of the tracked object. - */ -abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract TypedContent getHead(); - - abstract int len(); - - abstract DataFlowType getType(); - - abstract AccessPathFront getFront(); - - /** Gets the access path obtained by popping `head` from this path, if any. */ - abstract AccessPathApprox pop(TypedContent head); -} - -private class AccessPathApproxNil extends AccessPathApprox, TNil { - private DataFlowType t; - - AccessPathApproxNil() { this = TNil(t) } - - override string toString() { result = concat(": " + ppReprType(t)) } - - override TypedContent getHead() { none() } - - override int len() { result = 0 } - - override DataFlowType getType() { result = t } - - override AccessPathFront getFront() { result = TFrontNil(t) } - - override AccessPathApprox pop(TypedContent head) { none() } -} - -abstract private class AccessPathApproxCons extends AccessPathApprox { } - -private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private TypedContent tc; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(tc, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override TypedContent getHead() { result = tc } - - override int len() { result = 1 } - - override DataFlowType getType() { result = tc.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc) } - - override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } -} - -private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private TypedContent tc1; - private TypedContent tc2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } - - override string toString() { - if len = 2 - then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" - else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" - } - - override TypedContent getHead() { result = tc1 } - - override int len() { result = len } - - override DataFlowType getType() { result = tc1.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc1) } - - override AccessPathApprox pop(TypedContent head) { - head = tc1 and - ( - result = TConsCons(tc2, _, len - 1) - or - len = 2 and - result = TConsNil(tc2, _) - or - result = TCons1(tc2, len - 1) - ) - } -} - -private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private TypedContent tc; - private int len; - - AccessPathApproxCons1() { this = TCons1(tc, len) } - - override string toString() { - if len = 1 - then result = "[" + tc.toString() + "]" - else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" - } - - override TypedContent getHead() { result = tc } - - override int len() { result = len } - - override DataFlowType getType() { result = tc.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc) } - - override AccessPathApprox pop(TypedContent head) { - head = tc and - ( - exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | - result = TConsCons(tc2, _, len - 1) - or - len = 2 and - result = TConsNil(tc2, _) - or - result = TCons1(tc2, len - 1) - ) - or - exists(DataFlowType t | - len = 1 and - Stage3::consCand(tc, TFrontNil(t), _) and - result = TNil(t) - ) - ) - } -} - -/** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } - -/** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } - -private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - -private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } -} - -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - private ApApprox getApprox(Ap ap) { result = ap.getFront() } - - private ApNil getApNil(NodeEx node) { - PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) - } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } - - pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - class Cc = CallContext; - - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { - localFlowEntry(node, config) and - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc) - } - - pragma[nomagic] - private predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config - ) { - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and - PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, - Configuration config - ) { - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and - PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) - } - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { any() } - - // Type checking is not necessary here as it has already been done in stage 3. - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 4 logic. */ -} - -bindingset[conf, result] -private Configuration unbindConf(Configuration conf) { - exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) -} - -private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) { - exists(DataFlowCallable c, AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, apa, _) and - Stage4::revFlow(n, true, _, apa0, config) and - Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c - ) -} - -private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, AccessPath ap) { - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) - } - -/** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ -abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); -} - -/** A summary context from which no flow summary can be generated. */ -private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } -} - -/** A summary context from which a flow summary can be generated. */ -private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } - - int getParameterPos() { p.isParameterOf(_, result) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + ": " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ -private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { - exists(TypedContent tc, int len | - tc = apa.getHead() and - len = apa.len() and - result = - strictcount(AccessPathFront apf | - Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), - config) - ) - ) -} - -private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { - result = - strictcount(NodeEx n | - Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config) - ) -} - -/** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ -private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) -} - -private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { - exists(TypedContent head | - apa.pop(head) = result and - Stage4::consCand(head, result, config) - ) -} - -/** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ -private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - if apa.getHead().forceHighPrecision() - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) -} - -/** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ -private int countAps(AccessPathApprox apa, Configuration config) { - evalUnfold(apa, false, config) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) - or - evalUnfold(apa, false, config) and - result = count1to2unfold(apa, config) and - not expensiveLen1to2unfolding(apa, config) - or - evalUnfold(apa, true, config) and - result = countPotentialAps(apa, config) -} - -/** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ -language[monotonicAggregates] -private int countPotentialAps(AccessPathApprox apa, Configuration config) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) -} - -private newtype TAccessPath = - TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false, _) and - head = apa.getHead() and - tail.getApprox() = getATail(apa, _) - ) - } or - TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false, _) and - not expensiveLen1to2unfolding(apa, _) and - apa.len() = len and - head1 = apa.getHead() and - head2 = getATail(apa, _).getHead() - ) - } or - TAccessPathCons1(TypedContent head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false, _) and - expensiveLen1to2unfolding(apa, _) and - apa.len() = len and - head = apa.getHead() - ) - } - -private newtype TPathNode = - TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { - // A PathNode is introduced by a source ... - Stage4::revFlow(node, config) and - sourceNode(node, config) and - ( - if hasSourceCallCtx(config) - then cc instanceof CallContextSomeCall - else cc instanceof CallContextAny - ) and - sc instanceof SummaryCtxNone and - ap = TAccessPathNil(node.getDataFlowType()) - or - // ... or a step from an existing PathNode to another node. - exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and - pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config)) - ) - } or - TPathNodeSink(NodeEx node, Configuration config) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - config = sink.getConfiguration() - ) - } - -/** - * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a - * source to a given node with a given `AccessPath`, this indicates the sequence - * of dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. - */ -abstract private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract TypedContent getHead(); - - /** Gets the tail of this access path, if any. */ - abstract AccessPath getTail(); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - - /** Gets the access path obtained by popping `tc` from this access path, if any. */ - final AccessPath pop(TypedContent tc) { - result = this.getTail() and - tc = this.getHead() - } - - /** Gets the access path obtained by pushing `tc` onto this access path. */ - final AccessPath push(TypedContent tc) { this = result.pop(tc) } -} - -private class AccessPathNil extends AccessPath, TAccessPathNil { - private DataFlowType t; - - AccessPathNil() { this = TAccessPathNil(t) } - - DataFlowType getType() { result = t } - - override TypedContent getHead() { none() } - - override AccessPath getTail() { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil(t) } - - override AccessPathApproxNil getApprox() { result = TNil(t) } - - override int length() { result = 0 } - - override string toString() { result = concat(": " + ppReprType(t)) } -} - -private class AccessPathCons extends AccessPath, TAccessPathCons { - private TypedContent head; - private AccessPath tail; - - AccessPathCons() { this = TAccessPathCons(head, tail) } - - override TypedContent getHead() { result = head } - - override AccessPath getTail() { result = tail } - - override AccessPathFrontHead getFront() { result = TFrontHead(head) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head, tail.(AccessPathNil).getType()) - or - result = TConsCons(head, tail.getHead(), this.length()) - or - result = TCons1(head, this.length()) - } - - override int length() { result = 1 + tail.length() } - - private string toStringImpl(boolean needsSuffix) { - exists(DataFlowType t | - tail = TAccessPathNil(t) and - needsSuffix = false and - result = head.toString() + "]" + concat(" : " + ppReprType(t)) - ) - or - result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | - result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | - result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } -} - -private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private TypedContent head1; - private TypedContent head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } - - override TypedContent getHead() { result = head1 } - - override AccessPath getTail() { - Stage4::consCand(head1, result.getApprox(), _) and - result.getHead() = head2 and - result.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } -} - -private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private TypedContent head; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head, len) } - - override TypedContent getHead() { result = head } - - override AccessPath getTail() { - Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head) } - - override AccessPathApproxCons getApprox() { result = TCons1(head, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head.toString() + "]" - else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" - } -} - -/** - * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. - * Only those `PathNode`s that are reachable from a source are generated. - */ -class PathNode extends TPathNode { - /** Gets a textual representation of this element. */ - string toString() { none() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { none() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - none() - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } - - /** Gets the associated configuration. */ - Configuration getConfiguration() { none() } - - private PathNode getASuccessorIfHidden() { - this.(PathNodeImpl).isHidden() and - result = this.(PathNodeImpl).getASuccessorImpl() - } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { - result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and - not this.(PathNodeImpl).isHidden() and - not result.(PathNodeImpl).isHidden() - } - - /** Holds if this node is a source. */ - predicate isSource() { none() } -} - -abstract private class PathNodeImpl extends PathNode { - abstract PathNode getASuccessorImpl(); - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - - override string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** Holds if `n` can reach a sink. */ -private predicate directReach(PathNode n) { - n instanceof PathNodeSink or directReach(n.getASuccessor()) -} - -/** Holds if `n` can reach a sink or is used in a subpath. */ -private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } - -/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ -private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) } - -private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) - -/** - * Provides the query predicates needed to include a graph in a path-problem query. - */ -module PathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - reach(n) and key = "semmle.label" and val = n.toString() - } - - query predicate subpaths = Subpaths::subpaths/4; -} - -/** - * An intermediate flow graph node. This is a triple consisting of a `Node`, - * a `CallContext`, and a `Configuration`. - */ -private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - CallContext cc; - SummaryCtx sc; - AccessPath ap; - Configuration config; - - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } - - override NodeEx getNodeEx() { result = node } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - AccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(), - result.getAp()) and - result.getConfiguration() = unbindConf(this.getConfiguration()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, config) and - ( - if hasSourceCallCtx(config) - then cc instanceof CallContextSomeCall - else cc instanceof CallContextAny - ) and - sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil - } - - predicate isAtSink() { - sinkNode(node, config) and - ap instanceof AccessPathNil and - if hasSinkCallCtx(config) - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getConfiguration() = unbindConf(config) - } -} - -/** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ -private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - Configuration config; - - PathNodeSink() { this = TPathNodeSink(node, config) } - - override NodeEx getNodeEx() { result = node } - - override Configuration getConfiguration() { result = config } - - override PathNode getASuccessorImpl() { none() } - - override predicate isSource() { sourceNode(node, config) } -} - -/** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ -private predicate pathStep( - PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap -) { - exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNodeEx() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil - ) - or - jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(node.getDataFlowType()) - or - exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and - sc = mid.getSummaryCtx() - or - exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() - or - pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() -} - -pragma[nomagic] -private predicate pathReadStep( - PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc -) { - ap0 = mid.getAp() and - tc = ap0.getHead() and - Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and - cc = mid.getCallContext() -} - -pragma[nomagic] -private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc -) { - ap0 = mid.getAp() and - Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and - cc = mid.getCallContext() -} - -private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, - Configuration config -) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() and - config = mid.getConfiguration() -} - -pragma[nomagic] -private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, - Configuration config -) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, apa, config) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) -} - -pragma[noinline] -private NodeEx getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config -) { - result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, apa, config) -} - -/** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ -pragma[noinline] -private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, apa, config) and - out = getAnOutNodeFlow(kind, call, apa, config) - ) -} - -/** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ -pragma[noinline] -private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, - Configuration config -) { - exists(ArgNode arg | - arg = mid.getNodeEx().asNode() and - cc = mid.getCallContext() and - arg.argumentOf(call, i) and - ap = mid.getAp() and - apa = ap.getApprox() and - config = mid.getConfiguration() - ) -} - -pragma[nomagic] -private predicate parameterCand( - DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config -) { - exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, apa, config) and - p.isParameterOf(callable, i) - ) -} - -pragma[nomagic] -private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, Configuration config -) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), - pragma[only_bind_into](config)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), - pragma[only_bind_into](config)) - ) -} - -/** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ -pragma[nomagic] -private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call, Configuration config -) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ -pragma[nomagic] -private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, - Configuration config -) { - exists(PathNodeMid mid, RetNodeEx ret, int pos | - mid.getNodeEx() = ret and - kind = ret.getKind() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - config = mid.getConfiguration() and - ap = mid.getAp() and - apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) - ) -} - -pragma[nomagic] -private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa, Configuration config -) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call, config) and - paramFlowsThrough(kind, innercc, sc, ap, apa, config) - ) -} - -/** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ -pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | - pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and - out = getAnOutNodeFlow(kind, call, apa, config) - ) -} - -private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, AccessPath apout - ) { - exists(Configuration config | - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _, config) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and - not arg.isHidden() - ) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - pragma[nomagic] - private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, apout) and - ret.getNodeEx() = retnode and - kind = retnode.getKind() and - innercc = ret.getCallContext() and - sc = ret.getSummaryCtx() and - ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and - apout = ret.getAp() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, n2, _, _, _, _) or - store(n1, _, n2, _, _) or - read(n1, _, n2, _) - ) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) { - exists(ParamNodeEx p, NodeEx o, AccessPath apout | - pragma[only_bind_into](arg).getASuccessor() = par and - pragma[only_bind_into](arg).getASuccessor() = out and - subpaths03(arg, p, localStepToHidden*(ret), o, apout) and - not ret.isHidden() and - par.getNodeEx() = p and - out.getNodeEx() = o and - out.getAp() = apout - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath. - */ - predicate retReach(PathNode n) { - subpaths(_, _, n, _) - or - exists(PathNode mid | - retReach(mid) and - n.getASuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } -} - -/** - * Holds if data can flow (inter-procedurally) from `source` to `sink`. - * - * Will only have results if `configuration` has non-empty sources and - * sinks. - */ -private predicate flowsTo( - PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration -) { - flowsource.isSource() and - flowsource.getConfiguration() = configuration and - flowsource.(PathNodeImpl).getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink -} - -/** - * Holds if data can flow (inter-procedurally) from `source` to `sink`. - * - * Will only have results if `configuration` has non-empty sources and - * sinks. - */ -predicate flowsTo(Node source, Node sink, Configuration configuration) { - flowsTo(_, _, source, sink, configuration) -} - -private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - tuples = count(PathNode pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - tuples = count(PathNode pn | reach(pn)) -} - -/** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ -predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int tuples, Configuration config -) { - stage = "1 Fwd" and n = 10 and Stage1::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "1 Rev" and n = 15 and Stage1::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "2 Fwd" and n = 20 and Stage2::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "2 Rev" and n = 25 and Stage2::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "3 Fwd" and n = 30 and Stage3::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "3 Rev" and n = 35 and Stage3::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "4 Fwd" and n = 40 and Stage4::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "4 Rev" and n = 45 and Stage4::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, tuples) - or - stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, tuples) -} - -private module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { - exists(NodeEx node1, NodeEx node2 | - jumpStep(node1, node2, config) - or - additionalJumpStep(node1, node2, config) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { - exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | - interestingCallableSrc(mid, config) and callableStep(mid, c, config) - ) - } - - private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { - exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | - interestingCallableSink(mid, config) and callableStep(c, mid, config) - ) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c, Configuration config) { - interestingCallableSrc(c, config) or - interestingCallableSink(c, config) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | - callableStep(c1, c2, config) and - ce1 = TCallable(c1, pragma[only_bind_into](config)) and - ce2 = TCallable(c2, pragma[only_bind_into](config)) - ) - or - exists(Node n, Configuration config | - ce1 = TCallableSrc() and - config.isSource(n) and - ce2 = TCallable(getNodeEnclosingCallable(n), config) - ) - or - exists(Node n, Configuration config | - ce2 = TCallableSink() and - config.isSink(n) and - ce1 = TCallable(getNodeEnclosingCallable(n), config) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c, Configuration config) { - result = distSrcExt(TCallable(c, config)) - 1 - } - - private int distSink(DataFlowCallable c, Configuration config) { - result = distSinkExt(TCallable(c, config)) - 1 - } - - private newtype TPartialAccessPath = - TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first - * element of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - TypedContent getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil(_) and result = 0 - or - this = TPartialCons(_, result) - } - - DataFlowType getType() { - this = TPartialNil(result) - or - exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { - exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) - } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(TypedContent tc, int len | this = TPartialCons(tc, len) | - if len = 1 - then result = "[" + tc.toString() + "]" - else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private newtype TRevPartialAccessPath = - TRevPartialNil() or - TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class RevPartialAccessPath extends TRevPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TRevPartialCons(result, _) } - - int len() { - this = TRevPartialNil() and result = 0 - or - this = TRevPartialCons(_, result) - } - } - - private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { - override string toString() { result = "" } - } - - private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { - override string toString() { - exists(Content c, int len | this = TRevPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(RevPartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, - Configuration config - ) { - sourceNode(node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap = TPartialNil(node.getDataFlowType()) and - not fullBarrier(node, config) and - exists(config.explorationLimit()) - or - partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and - distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap, - Configuration config - ) { - sinkNode(node, config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = TRevPartialNil() and - not fullBarrier(node, config) and - exists(config.explorationLimit()) - or - exists(PartialPathNodeRev mid | - revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContentCached(node.asNode(), ap.getHead()) and - not fullBarrier(node, config) and - distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() - ) - } - - pragma[nomagic] - private predicate partialPathNodeMk0( - NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, - Configuration config - ) { - exists(PartialPathNodeFwd mid | - partialPathStep(mid, node, cc, sc1, sc2, ap, config) and - not fullBarrier(node, config) and - not clearsContentCached(node.asNode(), ap.getHead().getContent()) and - if node.asNode() instanceof CastingNode - then compatibleTypes(node.getDataFlowType(), ap.getType()) - else any() - ) - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets the associated configuration. */ - Configuration getConfiguration() { none() } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { - result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) - } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { - result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - PartialAccessPath ap; - Configuration config; - - PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) } - - NodeEx getNodeEx() { result = node } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - PartialAccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(), - result.getSummaryCtx2(), result.getAp(), result.getConfiguration()) - } - - predicate isSource() { - sourceNode(node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - RevPartialAccessPath ap; - Configuration config; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) } - - NodeEx getNodeEx() { result = node } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - RevPartialAccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(), - this.getAp(), this.getConfiguration()) - } - - predicate isSink() { - sinkNode(node, config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = TRevPartialNil() - } - } - - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - PartialAccessPath ap, Configuration config - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStep(mid.getNodeEx(), node, config) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalLocalFlowStep(mid.getNodeEx(), node, config) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(node.getDataFlowType()) and - config = mid.getConfiguration() - ) - or - jumpStep(mid.getNodeEx(), node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalJumpStep(mid.getNodeEx(), node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(node.getDataFlowType()) and - config = mid.getConfiguration() - or - partialPathStoreStep(mid, _, _, node, ap) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() - or - exists(PartialAccessPath ap0, TypedContent tc | - partialPathReadStep(mid, ap0, tc, node, cc, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, tc, ap0, config) - ) - or - partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) - or - partialPathOutOfCallable(mid, node, cc, ap, config) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() - or - partialPathThroughCallable(mid, node, cc, ap, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() - } - - bindingset[result, i] - private int unbindInt(int i) { i <= result and i >= result } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, - PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - store(midNode, tc, node, contentType, mid.getConfiguration()) and - ap2.getHead() = tc and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config - ) { - exists(PartialPathNodeFwd mid | - partialPathStoreStep(mid, ap1, tc, _, ap2) and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, - Configuration config - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and - ap.getHead() = tc and - pragma[only_bind_into](config) = mid.getConfiguration() and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap, - Configuration config - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - ap = mid.getAp() and - config = mid.getConfiguration() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, - PartialAccessPath ap, Configuration config - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, innercc, ap, config) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, - Configuration config - ) { - exists(ArgNode arg | - arg = mid.getNodeEx().asNode() and - cc = mid.getCallContext() and - arg.argumentOf(call, i) and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc, - DataFlowCall call, PartialAccessPath ap, Configuration config - ) { - partialPathIntoArg(mid, i, outercc, call, ap, config) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, - TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, - Configuration config - ) { - exists(int i, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and - p.isParameterOf(callable, i) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - PartialAccessPath ap, Configuration config - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, - PartialAccessPath ap, Configuration config - ) { - exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | - partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and - paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, cc, ap, config) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, - RevPartialAccessPath ap, Configuration config - ) { - localFlowStep(node, mid.getNodeEx(), config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalLocalFlowStep(node, mid.getNodeEx(), config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - mid.getAp() instanceof RevPartialAccessPathNil and - ap = TRevPartialNil() and - config = mid.getConfiguration() - or - jumpStep(node, mid.getNodeEx(), config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalJumpStep(node, mid.getNodeEx(), config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - mid.getAp() instanceof RevPartialAccessPathNil and - ap = TRevPartialNil() and - config = mid.getConfiguration() - or - revPartialPathReadStep(mid, _, _, node, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() - or - exists(RevPartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - apConsRev(ap, c, ap0, config) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, ap, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, - RevPartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode, mid.getConfiguration()) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev( - RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config - ) { - exists(PartialPathNodeRev mid | - revPartialPathReadStep(mid, ap1, c, _, ap2) and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config - ) { - exists(NodeEx midNode, TypedContent tc | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - store(node, tc, midNode, _, config) and - ap.getHead() = c and - config = mid.getConfiguration() and - tc.getContent() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - DataFlowCall call, RevPartialAccessPath ap, Configuration config - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(ap) and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, - Configuration config - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p | - mid.getNodeEx() = p and - p.getPosition() = pos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap, - Configuration config - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 | - revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and - revPartialPathFlowsThrough(pos, sc1, sc2, ap, config) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config - ) { - exists(DataFlowCall call, int pos | - revPartialPathThroughCallable0(call, mid, pos, ap, config) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } -} - -import FlowExploration - -private predicate partialFlow( - PartialPathNode source, PartialPathNode node, Configuration configuration -) { - source.getConfiguration() = configuration and - source.isFwdSource() and - node = source.getASuccessor+() -} - -private predicate revPartialFlow( - PartialPathNode node, PartialPathNode sink, Configuration configuration -) { - sink.getConfiguration() = configuration and - sink.isRevSink() and - node.getASuccessor+() = sink -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll deleted file mode 100644 index 42d7e89f2..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll +++ /dev/null @@ -1,4687 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Configuration` class. This file exists in several identical - * copies, allowing queries to use multiple `Configuration` classes that depend - * on each other without introducing mutual recursion among those configurations. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic - -/** - * A configuration of interprocedural data flow analysis. This defines - * sources, sinks, and any other configurable aspect of the analysis. Each - * use of the global data flow library must define its own unique extension - * of this abstract class. To create a configuration, extend this class with - * a subclass whose characteristic predicate is a unique singleton string. - * For example, write - * - * ```ql - * class MyAnalysisConfiguration extends DataFlow::Configuration { - * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } - * // Override `isSource` and `isSink`. - * // Optionally override `isBarrier`. - * // Optionally override `isAdditionalFlowStep`. - * } - * ``` - * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and - * the edges are those data-flow steps that preserve the value of the node - * along with any additional edges defined by `isAdditionalFlowStep`. - * Specifying nodes in `isBarrier` will remove those nodes from the graph, and - * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going - * and/or out-going edges from those nodes, respectively. - * - * Then, to query whether there is flow between some `source` and `sink`, - * write - * - * ```ql - * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) - * ``` - * - * Multiple configurations can coexist, but two classes extending - * `DataFlow::Configuration` should never depend on each other. One of them - * should instead depend on a `DataFlow2::Configuration`, a - * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. - */ -abstract class Configuration extends string { - bindingset[this] - Configuration() { any() } - - /** - * Holds if `source` is a relevant data flow source. - */ - abstract predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - abstract predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node) { none() } - - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ - predicate isBarrierGuard(BarrierGuard guard) { none() } - - /** - * Holds if the additional flow step from `node1` to `node2` must be taken - * into account in the analysis. - */ - predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, Content c) { none() } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - */ - FlowFeature getAFeature() { none() } - - /** - * Holds if data may flow from `source` to `sink` for this configuration. - */ - predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } - - /** - * Holds if data may flow from `source` to `sink` for this configuration. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } - - /** - * Holds if data may flow from some source to `sink` for this configuration. - */ - predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } - - /** - * Holds if data may flow from some source to `sink` for this configuration. - */ - predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } - - /** - * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` - * measured in approximate number of interprocedural steps. - */ - int explorationLimit() { none() } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * This predicate is disabled (has no results) by default. Override - * `explorationLimit()` with a suitable number to enable this predicate. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node, this) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * This predicate is disabled (has no results) by default. Override - * `explorationLimit()` with a suitable number to enable this predicate. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink, this) and - dist = node.getSinkDistance() - } -} - -/** - * This class exists to prevent mutual recursion between the user-overridden - * member predicates of `Configuration` and the rest of the data-flow library. - * Good performance cannot be guaranteed in the presence of such recursion, so - * it should be replaced by using more than one copy of the data flow library. - */ -abstract private class ConfigurationRecursionPrevention extends Configuration { - bindingset[this] - ConfigurationRecursionPrevention() { any() } - - override predicate hasFlow(Node source, Node sink) { - strictcount(Node n | this.isSource(n)) < 0 - or - strictcount(Node n | this.isSink(n)) < 0 - or - strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 - or - super.hasFlow(source, sink) - } -} - -private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] - } - -private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } -} - -private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, int i) { - this.asNode().(ParamNode).isParameterOf(c, i) - } - - int getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } -} - -private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } -} - -private predicate inBarrier(NodeEx node, Configuration config) { - exists(Node n | - node.asNode() = n and - config.isBarrierIn(n) and - config.isSource(n) - ) -} - -private predicate outBarrier(NodeEx node, Configuration config) { - exists(Node n | - node.asNode() = n and - config.isBarrierOut(n) and - config.isSink(n) - ) -} - -pragma[nomagic] -private predicate fullBarrier(NodeEx node, Configuration config) { - exists(Node n | node.asNode() = n | - config.isBarrier(n) - or - config.isBarrierIn(n) and - not config.isSource(n) - or - config.isBarrierOut(n) and - not config.isSink(n) - or - exists(BarrierGuard g | - config.isBarrierGuard(g) and - n = g.getAGuardedNode() - ) - ) -} - -pragma[nomagic] -private predicate sourceNode(NodeEx node, Configuration config) { - config.isSource(node.asNode()) and - not fullBarrier(node, config) -} - -pragma[nomagic] -private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) } - -/** Provides the relevant barriers for a step from `node1` to `node2`. */ -pragma[inline] -private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { - not outBarrier(node1, config) and - not inBarrier(node2, config) and - not fullBarrier(node1, config) and - not fullBarrier(node2, config) -} - -/** - * Holds if data can flow in one local step from `node1` to `node2`. - */ -private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(n1, n2) and - stepFilter(node1, node2, config) - ) - or - exists(Node n | - config.allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1, config) - ) -} - -/** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ -private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - config.isAdditionalFlowStep(n1, n2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2, config) - ) - or - exists(Node n | - config.allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2, config) - ) -} - -/** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ -private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(n1, n2) and - stepFilter(node1, node2, config) and - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) -} - -/** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ -private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - config.isAdditionalFlowStep(n1, n2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2, config) and - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) -} - -private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { - read(node1.asNode(), c, node2.asNode()) and - stepFilter(node1, node2, config) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - config.allowImplicitRead(n, c) - ) -} - -private predicate store( - NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config -) { - store(node1.asNode(), tc, node2.asNode(), contentType) and - read(_, tc.getContent(), _, config) and - stepFilter(node1, node2, config) -} - -pragma[nomagic] -private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) -} - -pragma[nomagic] -private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) -} - -/** - * Holds if field flow should be used for the given configuration. - */ -private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } - -private predicate hasSourceCallCtx(Configuration config) { - exists(FlowFeature feature | feature = config.getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) -} - -private predicate hasSinkCallCtx(Configuration config) { - exists(FlowFeature feature | feature = config.getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) -} - -private module Stage1 { - class ApApprox = Unit; - - class Ap = Unit; - - class ApOption = Unit; - - class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source in the configuration `config`. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { - sourceNode(node, config) and - if hasSourceCallCtx(config) then cc = true else cc = false - or - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - localFlowStep(mid, node, config) - ) - or - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - additionalLocalFlowStep(mid, node, config) - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, config) and - jumpStep(mid, node, config) and - cc = false - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, config) and - additionalJumpStep(mid, node, config) and - cc = false - ) - or - // store - exists(NodeEx mid | - useFieldFlow(config) and - fwdFlow(mid, cc, config) and - store(mid, _, node, _, config) - ) - or - // read - exists(Content c | - fwdFlowRead(c, node, cc, config) and - fwdFlowConsCand(c, config) - ) - or - // flow into a callable - exists(NodeEx arg | - fwdFlow(arg, _, config) and - viableParamArgEx(_, node, arg) and - cc = true and - not fullBarrier(node, config) - ) - or - // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, false, config) and - cc = false - or - fwdFlowOutFromArg(call, node, config) and - fwdFlowIsEntered(call, cc, config) - ) - } - - private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } - - pragma[nomagic] - private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) { - exists(NodeEx mid | - fwdFlow(mid, cc, config) and - read(mid, c, node, config) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c, Configuration config) { - exists(NodeEx mid, NodeEx node, TypedContent tc | - not fullBarrier(node, config) and - useFieldFlow(config) and - fwdFlow(mid, _, config) and - store(mid, tc, node, _, config) and - c = tc.getContent() - ) - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { - exists(RetNodeEx ret | - fwdFlow(ret, cc, config) and - ret.getReturnPosition() = pos - ) - } - - pragma[nomagic] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc, config) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { - fwdFlowOut(call, out, true, config) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgNodeEx arg | - fwdFlow(arg, cc, config) and - viableParamArgEx(call, _, arg) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink in the - * configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { - revFlow0(node, toReturn, config) and - fwdFlow(node, config) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { - fwdFlow(node, config) and - sinkNode(node, config) and - if hasSinkCallCtx(config) then toReturn = true else toReturn = false - or - exists(NodeEx mid | - localFlowStep(node, mid, config) and - revFlow(mid, toReturn, config) - ) - or - exists(NodeEx mid | - additionalLocalFlowStep(node, mid, config) and - revFlow(mid, toReturn, config) - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, config) and - toReturn = false - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid, config) and - revFlow(mid, _, config) and - toReturn = false - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn, config) and - revFlowConsCand(c, config) - ) - or - // read - exists(NodeEx mid, Content c | - read(node, c, mid, config) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - revFlow(mid, toReturn, pragma[only_bind_into](config)) - ) - or - // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, false, config) and - toReturn = false - or - revFlowInToReturn(call, node, config) and - revFlowIsReturned(call, toReturn, config) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos, config) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c, Configuration config) { - exists(NodeEx mid, NodeEx node | - fwdFlow(node, pragma[only_bind_into](config)) and - read(node, c, mid, config) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { - exists(NodeEx mid, TypedContent tc | - revFlow(mid, toReturn, pragma[only_bind_into](config)) and - fwdFlowConsCand(c, pragma[only_bind_into](config)) and - store(node, tc, mid, _, config) and - c = tc.getContent() - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { - revFlowConsCand(c, conf) and - revFlowStore(c, _, _, conf) - } - - pragma[nomagic] - predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config - ) { - fwdFlowReturnPosition(pos, _, config) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos, Configuration config) { - exists(DataFlowCall call, NodeEx out | - revFlow(out, _, config) and - viableReturnPosOutNodeCandFwd1(call, pos, out, config) - ) - } - - pragma[nomagic] - predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config - ) { - viableParamArgEx(call, p, arg) and - fwdFlow(arg, config) - } - - pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config - ) { - exists(ParamNodeEx p | - revFlow(p, toReturn, config) and - viableParamArgNodeCandFwd1(call, p, arg, config) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { - revFlowIn(call, arg, true, config) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { - exists(NodeEx out | - revFlow(out, toReturn, config) and - fwdFlowOutFromArg(call, out, config) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Content c | - revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and - revFlow(node2, pragma[only_bind_into](config)) and - store(node1, tc, node2, contentType, config) and - c = tc.getContent() and - exists(ap1) - ) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and - revFlow(n2, pragma[only_bind_into](config)) and - read(n1, c, n2, pragma[only_bind_into](config)) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } - - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow(node, toReturn, config) and exists(returnAp) and exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node, Configuration config) { - revFlow(node, true, config) and - fwdFlow(node, true, config) and - not inBarrier(node, config) and - not outBarrier(node, config) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand( - DataFlowCallable callable, ReturnKindExt kind, Configuration config - ) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret, config) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | - throughFlowNodeCand(p, config) and - returnFlowCallableNodeCand(c, kind, config) and - p.getEnclosingCallable() = c and - exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn, config) and - revFlowInToReturn(call, arg, config) and - revFlowIsReturned(call, toReturn, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, config)) and - fields = count(Content f0 | fwdFlowConsCand(f0, config)) and - conscand = -1 and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, config)) and - fields = count(Content f0 | revFlowConsCand(f0, config)) and - conscand = -1 and - tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) - } - /* End: Stage 1 logic. */ -} - -pragma[noinline] -private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { - Stage1::revFlow(node2, config) and - localFlowStep(node1, node2, config) -} - -pragma[noinline] -private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { - Stage1::revFlow(node2, config) and - additionalLocalFlowStep(node1, node2, config) -} - -pragma[nomagic] -private predicate viableReturnPosOutNodeCand1( - DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config -) { - Stage1::revFlow(out, config) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) -} - -/** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ -pragma[nomagic] -private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config -) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) -} - -pragma[nomagic] -private predicate viableParamArgNodeCand1( - DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config -) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and - Stage1::revFlow(arg, config) -} - -/** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ -pragma[nomagic] -private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config -) { - viableParamArgNodeCand1(call, p, arg, config) and - Stage1::revFlow(p, config) and - not outBarrier(arg, config) and - not inBarrier(p, config) -} - -/** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ -private int branch(NodeEx n1, Configuration conf) { - result = - strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - ) -} - -/** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ -private int join(NodeEx n2, Configuration conf) { - result = - strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - ) -} - -/** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ -pragma[nomagic] -private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config -) { - flowOutOfCallNodeCand1(call, ret, out, config) and - exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) -} - -/** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ -pragma[nomagic] -private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config -) { - flowIntoCallNodeCand1(call, arg, p, config) and - exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) -} - -private module Stage2 { - module PrevStage = Stage1; - - class ApApprox = PrevStage::Ap; - - class Ap = boolean; - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } - - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } - - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - class Cc = CallContext; - - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2, config) - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2, config) - ) and - exists(ap) and - exists(lcc) - } - - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - private predicate flowIntoCall = flowIntoCallNodeCand1/5; - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { any() } - - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 2 logic. */ -} - -pragma[nomagic] -private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config -) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) and - Stage2::revFlow(node1, pragma[only_bind_into](config)) -} - -pragma[nomagic] -private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, - Configuration config -) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) and - Stage2::revFlow(node1, pragma[only_bind_into](config)) -} - -private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - predicate localFlowEntry(NodeEx node, Configuration config) { - Stage2::revFlow(node, config) and - ( - sourceNode(node, config) or - jumpStep(_, node, config) or - additionalJumpStep(_, node, config) or - node instanceof ParamNodeEx or - node.asNode() instanceof OutNodeExt or - store(_, _, node, _, config) or - read(_, _, node, config) or - node instanceof FlowCheckNode - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, Configuration config) { - exists(NodeEx next | Stage2::revFlow(next, config) | - jumpStep(node, next, config) or - additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next, _, config) or - read(node, _, next, config) - ) - or - node instanceof FlowCheckNode - or - sinkNode(node, config) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, NodeEx node2, Configuration config - ) { - additionalLocalFlowStepNodeCand1(node1, node2, config) and - Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config)) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config, - LocalCallContext cc - ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and - ( - localFlowEntry(node1, pragma[only_bind_into](config)) and - ( - localFlowStepNodeCand1(node1, node2, config) and - preservesValue = true and - t = node1.getDataFlowType() // irrelevant dummy value - or - additionalLocalFlowStepNodeCand2(node1, node2, config) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - or - exists(NodeEx mid | - localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and - localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and - additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() and - Stage2::revFlow(node2, pragma[only_bind_into](config)) - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf, - Configuration config, LocalCallContext callContext - ) { - localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and - localFlowExit(node2, config) - } -} - -private import LocalFlowBigStep - -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - private ApNil getApNil(NodeEx node) { - PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) - } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } - - pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - class Cc = boolean; - - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc) - } - - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - private predicate flowIntoCall = flowIntoCallNodeCand2/5; - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) } - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { - not clear(node, ap) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() - } - - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(ap.getType(), contentType) - } - - /* Begin: Stage 3 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 3 logic. */ -} - -/** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ -private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) { - exists(AccessPathFront apf | - Stage3::revFlow(node, true, _, apf, config) and - Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) - ) -} - -/** - * Holds if a length 2 access path approximation with the head `tc` is expected - * to be expensive. - */ -private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and - nodes = - strictcount(NodeEx n | - Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) - or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not tc.forceHighPrecision() - ) -} - -private newtype TAccessPathApprox = - TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { - Stage3::consCand(tc, TFrontNil(t), _) and - not expensiveLen2unfolding(tc, _) - } or - TConsCons(TypedContent tc1, TypedContent tc2, int len) { - Stage3::consCand(tc1, TFrontHead(tc2), _) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(tc1, _) - } or - TCons1(TypedContent tc, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(tc, _) - } - -/** - * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only - * the first two elements of the list and its length are tracked. If data flows - * from a source to a given node with a given `AccessPathApprox`, this indicates - * the sequence of dereference operations needed to get from the value in the node - * to the tracked object. The final type indicates the type of the tracked object. - */ -abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract TypedContent getHead(); - - abstract int len(); - - abstract DataFlowType getType(); - - abstract AccessPathFront getFront(); - - /** Gets the access path obtained by popping `head` from this path, if any. */ - abstract AccessPathApprox pop(TypedContent head); -} - -private class AccessPathApproxNil extends AccessPathApprox, TNil { - private DataFlowType t; - - AccessPathApproxNil() { this = TNil(t) } - - override string toString() { result = concat(": " + ppReprType(t)) } - - override TypedContent getHead() { none() } - - override int len() { result = 0 } - - override DataFlowType getType() { result = t } - - override AccessPathFront getFront() { result = TFrontNil(t) } - - override AccessPathApprox pop(TypedContent head) { none() } -} - -abstract private class AccessPathApproxCons extends AccessPathApprox { } - -private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private TypedContent tc; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(tc, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override TypedContent getHead() { result = tc } - - override int len() { result = 1 } - - override DataFlowType getType() { result = tc.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc) } - - override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } -} - -private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private TypedContent tc1; - private TypedContent tc2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } - - override string toString() { - if len = 2 - then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" - else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" - } - - override TypedContent getHead() { result = tc1 } - - override int len() { result = len } - - override DataFlowType getType() { result = tc1.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc1) } - - override AccessPathApprox pop(TypedContent head) { - head = tc1 and - ( - result = TConsCons(tc2, _, len - 1) - or - len = 2 and - result = TConsNil(tc2, _) - or - result = TCons1(tc2, len - 1) - ) - } -} - -private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private TypedContent tc; - private int len; - - AccessPathApproxCons1() { this = TCons1(tc, len) } - - override string toString() { - if len = 1 - then result = "[" + tc.toString() + "]" - else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" - } - - override TypedContent getHead() { result = tc } - - override int len() { result = len } - - override DataFlowType getType() { result = tc.getContainerType() } - - override AccessPathFront getFront() { result = TFrontHead(tc) } - - override AccessPathApprox pop(TypedContent head) { - head = tc and - ( - exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | - result = TConsCons(tc2, _, len - 1) - or - len = 2 and - result = TConsNil(tc2, _) - or - result = TCons1(tc2, len - 1) - ) - or - exists(DataFlowType t | - len = 1 and - Stage3::consCand(tc, TFrontNil(t), _) and - result = TNil(t) - ) - ) - } -} - -/** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } - -/** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } - -private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - -private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } -} - -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - private ApApprox getApprox(Ap ap) { result = ap.getFront() } - - private ApNil getApNil(NodeEx node) { - PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) - } - - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } - - pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - class Cc = CallContext; - - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc, config] - private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { - localFlowEntry(node, config) and - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( - NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc - ) { - localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc) - } - - pragma[nomagic] - private predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config - ) { - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and - PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, - Configuration config - ) { - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and - PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) - } - - bindingset[node, ap] - private predicate filter(NodeEx node, Ap ap) { any() } - - // Type checking is not necessary here as it has already been done in stage 3. - bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - exists(ApApprox apa0 | - apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) - ) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, cc, argAp, ap, config) and - flowCand(node, unbindApa(getApprox(ap)), config) and - filter(node, ap) - } - - pragma[nomagic] - private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { - flowCand(node, _, config) and - sourceNode(node, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, Ap ap0, LocalCc localCc | - fwdFlow(mid, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc, config) - | - localStep(mid, node, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, node, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and - flowCand(node, _, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config - ) { - fwdFlow(node1, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, - Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) { - fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { - revFlow0(node, toReturn, returnAp, ap, config) and - fwdFlow(node, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, _, _, ap, config) and - sinkNode(node, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid | - localStep(node, mid, true, _, config, _) and - revFlow(mid, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - localStep(node, mid, false, _, config, _) and - revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, - ApOption returnAp, Configuration config - ) { - revFlow(mid, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, CcCall ccc | - revFlowOut(call, ret, toReturn, returnAp, ap, config) and - fwdFlow(ret, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - store(node1, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), - pragma[only_bind_into](config)) and - fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | - revFlow(arg, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) - } - /* End: Stage 4 logic. */ -} - -bindingset[conf, result] -private Configuration unbindConf(Configuration conf) { - exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) -} - -private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) { - exists(DataFlowCallable c, AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, apa, _) and - Stage4::revFlow(n, true, _, apa0, config) and - Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c - ) -} - -private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, AccessPath ap) { - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) - } - -/** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ -abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); -} - -/** A summary context from which no flow summary can be generated. */ -private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } -} - -/** A summary context from which a flow summary can be generated. */ -private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } - - int getParameterPos() { p.isParameterOf(_, result) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + ": " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ -private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { - exists(TypedContent tc, int len | - tc = apa.getHead() and - len = apa.len() and - result = - strictcount(AccessPathFront apf | - Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), - config) - ) - ) -} - -private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { - result = - strictcount(NodeEx n | - Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config) - ) -} - -/** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ -private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) -} - -private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { - exists(TypedContent head | - apa.pop(head) = result and - Stage4::consCand(head, result, config) - ) -} - -/** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ -private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - if apa.getHead().forceHighPrecision() - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) -} - -/** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ -private int countAps(AccessPathApprox apa, Configuration config) { - evalUnfold(apa, false, config) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) - or - evalUnfold(apa, false, config) and - result = count1to2unfold(apa, config) and - not expensiveLen1to2unfolding(apa, config) - or - evalUnfold(apa, true, config) and - result = countPotentialAps(apa, config) -} - -/** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ -language[monotonicAggregates] -private int countPotentialAps(AccessPathApprox apa, Configuration config) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) -} - -private newtype TAccessPath = - TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false, _) and - head = apa.getHead() and - tail.getApprox() = getATail(apa, _) - ) - } or - TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false, _) and - not expensiveLen1to2unfolding(apa, _) and - apa.len() = len and - head1 = apa.getHead() and - head2 = getATail(apa, _).getHead() - ) - } or - TAccessPathCons1(TypedContent head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false, _) and - expensiveLen1to2unfolding(apa, _) and - apa.len() = len and - head = apa.getHead() - ) - } - -private newtype TPathNode = - TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { - // A PathNode is introduced by a source ... - Stage4::revFlow(node, config) and - sourceNode(node, config) and - ( - if hasSourceCallCtx(config) - then cc instanceof CallContextSomeCall - else cc instanceof CallContextAny - ) and - sc instanceof SummaryCtxNone and - ap = TAccessPathNil(node.getDataFlowType()) - or - // ... or a step from an existing PathNode to another node. - exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and - pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config)) - ) - } or - TPathNodeSink(NodeEx node, Configuration config) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - config = sink.getConfiguration() - ) - } - -/** - * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a - * source to a given node with a given `AccessPath`, this indicates the sequence - * of dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. - */ -abstract private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract TypedContent getHead(); - - /** Gets the tail of this access path, if any. */ - abstract AccessPath getTail(); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - - /** Gets the access path obtained by popping `tc` from this access path, if any. */ - final AccessPath pop(TypedContent tc) { - result = this.getTail() and - tc = this.getHead() - } - - /** Gets the access path obtained by pushing `tc` onto this access path. */ - final AccessPath push(TypedContent tc) { this = result.pop(tc) } -} - -private class AccessPathNil extends AccessPath, TAccessPathNil { - private DataFlowType t; - - AccessPathNil() { this = TAccessPathNil(t) } - - DataFlowType getType() { result = t } - - override TypedContent getHead() { none() } - - override AccessPath getTail() { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil(t) } - - override AccessPathApproxNil getApprox() { result = TNil(t) } - - override int length() { result = 0 } - - override string toString() { result = concat(": " + ppReprType(t)) } -} - -private class AccessPathCons extends AccessPath, TAccessPathCons { - private TypedContent head; - private AccessPath tail; - - AccessPathCons() { this = TAccessPathCons(head, tail) } - - override TypedContent getHead() { result = head } - - override AccessPath getTail() { result = tail } - - override AccessPathFrontHead getFront() { result = TFrontHead(head) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head, tail.(AccessPathNil).getType()) - or - result = TConsCons(head, tail.getHead(), this.length()) - or - result = TCons1(head, this.length()) - } - - override int length() { result = 1 + tail.length() } - - private string toStringImpl(boolean needsSuffix) { - exists(DataFlowType t | - tail = TAccessPathNil(t) and - needsSuffix = false and - result = head.toString() + "]" + concat(" : " + ppReprType(t)) - ) - or - result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | - result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | - result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } -} - -private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private TypedContent head1; - private TypedContent head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } - - override TypedContent getHead() { result = head1 } - - override AccessPath getTail() { - Stage4::consCand(head1, result.getApprox(), _) and - result.getHead() = head2 and - result.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } -} - -private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private TypedContent head; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head, len) } - - override TypedContent getHead() { result = head } - - override AccessPath getTail() { - Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head) } - - override AccessPathApproxCons getApprox() { result = TCons1(head, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head.toString() + "]" - else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" - } -} - -/** - * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. - * Only those `PathNode`s that are reachable from a source are generated. - */ -class PathNode extends TPathNode { - /** Gets a textual representation of this element. */ - string toString() { none() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { none() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - none() - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } - - /** Gets the associated configuration. */ - Configuration getConfiguration() { none() } - - private PathNode getASuccessorIfHidden() { - this.(PathNodeImpl).isHidden() and - result = this.(PathNodeImpl).getASuccessorImpl() - } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { - result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and - not this.(PathNodeImpl).isHidden() and - not result.(PathNodeImpl).isHidden() - } - - /** Holds if this node is a source. */ - predicate isSource() { none() } -} - -abstract private class PathNodeImpl extends PathNode { - abstract PathNode getASuccessorImpl(); - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - - override string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** Holds if `n` can reach a sink. */ -private predicate directReach(PathNode n) { - n instanceof PathNodeSink or directReach(n.getASuccessor()) -} - -/** Holds if `n` can reach a sink or is used in a subpath. */ -private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } - -/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ -private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) } - -private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) - -/** - * Provides the query predicates needed to include a graph in a path-problem query. - */ -module PathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - reach(n) and key = "semmle.label" and val = n.toString() - } - - query predicate subpaths = Subpaths::subpaths/4; -} - -/** - * An intermediate flow graph node. This is a triple consisting of a `Node`, - * a `CallContext`, and a `Configuration`. - */ -private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - CallContext cc; - SummaryCtx sc; - AccessPath ap; - Configuration config; - - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } - - override NodeEx getNodeEx() { result = node } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - AccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(), - result.getAp()) and - result.getConfiguration() = unbindConf(this.getConfiguration()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, config) and - ( - if hasSourceCallCtx(config) - then cc instanceof CallContextSomeCall - else cc instanceof CallContextAny - ) and - sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil - } - - predicate isAtSink() { - sinkNode(node, config) and - ap instanceof AccessPathNil and - if hasSinkCallCtx(config) - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getConfiguration() = unbindConf(config) - } -} - -/** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ -private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - Configuration config; - - PathNodeSink() { this = TPathNodeSink(node, config) } - - override NodeEx getNodeEx() { result = node } - - override Configuration getConfiguration() { result = config } - - override PathNode getASuccessorImpl() { none() } - - override predicate isSource() { sourceNode(node, config) } -} - -/** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ -private predicate pathStep( - PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap -) { - exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNodeEx() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil - ) - or - jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(node.getDataFlowType()) - or - exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and - sc = mid.getSummaryCtx() - or - exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() - or - pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() -} - -pragma[nomagic] -private predicate pathReadStep( - PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc -) { - ap0 = mid.getAp() and - tc = ap0.getHead() and - Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and - cc = mid.getCallContext() -} - -pragma[nomagic] -private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc -) { - ap0 = mid.getAp() and - Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and - cc = mid.getCallContext() -} - -private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, - Configuration config -) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() and - config = mid.getConfiguration() -} - -pragma[nomagic] -private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, - Configuration config -) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, apa, config) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) -} - -pragma[noinline] -private NodeEx getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config -) { - result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, apa, config) -} - -/** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ -pragma[noinline] -private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, apa, config) and - out = getAnOutNodeFlow(kind, call, apa, config) - ) -} - -/** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ -pragma[noinline] -private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, - Configuration config -) { - exists(ArgNode arg | - arg = mid.getNodeEx().asNode() and - cc = mid.getCallContext() and - arg.argumentOf(call, i) and - ap = mid.getAp() and - apa = ap.getApprox() and - config = mid.getConfiguration() - ) -} - -pragma[nomagic] -private predicate parameterCand( - DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config -) { - exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, apa, config) and - p.isParameterOf(callable, i) - ) -} - -pragma[nomagic] -private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, Configuration config -) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), - pragma[only_bind_into](config)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), - pragma[only_bind_into](config)) - ) -} - -/** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ -pragma[nomagic] -private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call, Configuration config -) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ -pragma[nomagic] -private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, - Configuration config -) { - exists(PathNodeMid mid, RetNodeEx ret, int pos | - mid.getNodeEx() = ret and - kind = ret.getKind() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - config = mid.getConfiguration() and - ap = mid.getAp() and - apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) - ) -} - -pragma[nomagic] -private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa, Configuration config -) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call, config) and - paramFlowsThrough(kind, innercc, sc, ap, apa, config) - ) -} - -/** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ -pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | - pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and - out = getAnOutNodeFlow(kind, call, apa, config) - ) -} - -private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, AccessPath apout - ) { - exists(Configuration config | - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _, config) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and - not arg.isHidden() - ) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - pragma[nomagic] - private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, apout) and - ret.getNodeEx() = retnode and - kind = retnode.getKind() and - innercc = ret.getCallContext() and - sc = ret.getSummaryCtx() and - ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and - apout = ret.getAp() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, n2, _, _, _, _) or - store(n1, _, n2, _, _) or - read(n1, _, n2, _) - ) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) { - exists(ParamNodeEx p, NodeEx o, AccessPath apout | - pragma[only_bind_into](arg).getASuccessor() = par and - pragma[only_bind_into](arg).getASuccessor() = out and - subpaths03(arg, p, localStepToHidden*(ret), o, apout) and - not ret.isHidden() and - par.getNodeEx() = p and - out.getNodeEx() = o and - out.getAp() = apout - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath. - */ - predicate retReach(PathNode n) { - subpaths(_, _, n, _) - or - exists(PathNode mid | - retReach(mid) and - n.getASuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } -} - -/** - * Holds if data can flow (inter-procedurally) from `source` to `sink`. - * - * Will only have results if `configuration` has non-empty sources and - * sinks. - */ -private predicate flowsTo( - PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration -) { - flowsource.isSource() and - flowsource.getConfiguration() = configuration and - flowsource.(PathNodeImpl).getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink -} - -/** - * Holds if data can flow (inter-procedurally) from `source` to `sink`. - * - * Will only have results if `configuration` has non-empty sources and - * sinks. - */ -predicate flowsTo(Node source, Node sink, Configuration configuration) { - flowsTo(_, _, source, sink, configuration) -} - -private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - tuples = count(PathNode pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - tuples = count(PathNode pn | reach(pn)) -} - -/** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ -predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int tuples, Configuration config -) { - stage = "1 Fwd" and n = 10 and Stage1::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "1 Rev" and n = 15 and Stage1::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "2 Fwd" and n = 20 and Stage2::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "2 Rev" and n = 25 and Stage2::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "3 Fwd" and n = 30 and Stage3::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "3 Rev" and n = 35 and Stage3::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "4 Fwd" and n = 40 and Stage4::stats(true, nodes, fields, conscand, tuples, config) - or - stage = "4 Rev" and n = 45 and Stage4::stats(false, nodes, fields, conscand, tuples, config) - or - stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, tuples) - or - stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, tuples) -} - -private module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { - exists(NodeEx node1, NodeEx node2 | - jumpStep(node1, node2, config) - or - additionalJumpStep(node1, node2, config) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { - exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | - interestingCallableSrc(mid, config) and callableStep(mid, c, config) - ) - } - - private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { - exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | - interestingCallableSink(mid, config) and callableStep(c, mid, config) - ) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c, Configuration config) { - interestingCallableSrc(c, config) or - interestingCallableSink(c, config) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | - callableStep(c1, c2, config) and - ce1 = TCallable(c1, pragma[only_bind_into](config)) and - ce2 = TCallable(c2, pragma[only_bind_into](config)) - ) - or - exists(Node n, Configuration config | - ce1 = TCallableSrc() and - config.isSource(n) and - ce2 = TCallable(getNodeEnclosingCallable(n), config) - ) - or - exists(Node n, Configuration config | - ce2 = TCallableSink() and - config.isSink(n) and - ce1 = TCallable(getNodeEnclosingCallable(n), config) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c, Configuration config) { - result = distSrcExt(TCallable(c, config)) - 1 - } - - private int distSink(DataFlowCallable c, Configuration config) { - result = distSinkExt(TCallable(c, config)) - 1 - } - - private newtype TPartialAccessPath = - TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first - * element of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - TypedContent getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil(_) and result = 0 - or - this = TPartialCons(_, result) - } - - DataFlowType getType() { - this = TPartialNil(result) - or - exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { - exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) - } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(TypedContent tc, int len | this = TPartialCons(tc, len) | - if len = 1 - then result = "[" + tc.toString() + "]" - else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private newtype TRevPartialAccessPath = - TRevPartialNil() or - TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class RevPartialAccessPath extends TRevPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TRevPartialCons(result, _) } - - int len() { - this = TRevPartialNil() and result = 0 - or - this = TRevPartialCons(_, result) - } - } - - private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { - override string toString() { result = "" } - } - - private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { - override string toString() { - exists(Content c, int len | this = TRevPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(RevPartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, - Configuration config - ) { - sourceNode(node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap = TPartialNil(node.getDataFlowType()) and - not fullBarrier(node, config) and - exists(config.explorationLimit()) - or - partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and - distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap, - Configuration config - ) { - sinkNode(node, config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = TRevPartialNil() and - not fullBarrier(node, config) and - exists(config.explorationLimit()) - or - exists(PartialPathNodeRev mid | - revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContentCached(node.asNode(), ap.getHead()) and - not fullBarrier(node, config) and - distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() - ) - } - - pragma[nomagic] - private predicate partialPathNodeMk0( - NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, - Configuration config - ) { - exists(PartialPathNodeFwd mid | - partialPathStep(mid, node, cc, sc1, sc2, ap, config) and - not fullBarrier(node, config) and - not clearsContentCached(node.asNode(), ap.getHead().getContent()) and - if node.asNode() instanceof CastingNode - then compatibleTypes(node.getDataFlowType(), ap.getType()) - else any() - ) - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets the associated configuration. */ - Configuration getConfiguration() { none() } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { - result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) - } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { - result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - PartialAccessPath ap; - Configuration config; - - PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) } - - NodeEx getNodeEx() { result = node } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - PartialAccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(), - result.getSummaryCtx2(), result.getAp(), result.getConfiguration()) - } - - predicate isSource() { - sourceNode(node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - RevPartialAccessPath ap; - Configuration config; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) } - - NodeEx getNodeEx() { result = node } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - RevPartialAccessPath getAp() { result = ap } - - override Configuration getConfiguration() { result = config } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(), - this.getAp(), this.getConfiguration()) - } - - predicate isSink() { - sinkNode(node, config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = TRevPartialNil() - } - } - - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - PartialAccessPath ap, Configuration config - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStep(mid.getNodeEx(), node, config) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalLocalFlowStep(mid.getNodeEx(), node, config) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(node.getDataFlowType()) and - config = mid.getConfiguration() - ) - or - jumpStep(mid.getNodeEx(), node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalJumpStep(mid.getNodeEx(), node, config) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(node.getDataFlowType()) and - config = mid.getConfiguration() - or - partialPathStoreStep(mid, _, _, node, ap) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() - or - exists(PartialAccessPath ap0, TypedContent tc | - partialPathReadStep(mid, ap0, tc, node, cc, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, tc, ap0, config) - ) - or - partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) - or - partialPathOutOfCallable(mid, node, cc, ap, config) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() - or - partialPathThroughCallable(mid, node, cc, ap, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() - } - - bindingset[result, i] - private int unbindInt(int i) { i <= result and i >= result } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, - PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - store(midNode, tc, node, contentType, mid.getConfiguration()) and - ap2.getHead() = tc and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config - ) { - exists(PartialPathNodeFwd mid | - partialPathStoreStep(mid, ap1, tc, _, ap2) and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, - Configuration config - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and - ap.getHead() = tc and - pragma[only_bind_into](config) = mid.getConfiguration() and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap, - Configuration config - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - ap = mid.getAp() and - config = mid.getConfiguration() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, - PartialAccessPath ap, Configuration config - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, innercc, ap, config) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, - Configuration config - ) { - exists(ArgNode arg | - arg = mid.getNodeEx().asNode() and - cc = mid.getCallContext() and - arg.argumentOf(call, i) and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc, - DataFlowCall call, PartialAccessPath ap, Configuration config - ) { - partialPathIntoArg(mid, i, outercc, call, ap, config) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, - TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, - Configuration config - ) { - exists(int i, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and - p.isParameterOf(callable, i) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - PartialAccessPath ap, Configuration config - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, - PartialAccessPath ap, Configuration config - ) { - exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | - partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and - paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, cc, ap, config) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, - RevPartialAccessPath ap, Configuration config - ) { - localFlowStep(node, mid.getNodeEx(), config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalLocalFlowStep(node, mid.getNodeEx(), config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - mid.getAp() instanceof RevPartialAccessPathNil and - ap = TRevPartialNil() and - config = mid.getConfiguration() - or - jumpStep(node, mid.getNodeEx(), config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - or - additionalJumpStep(node, mid.getNodeEx(), config) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - mid.getAp() instanceof RevPartialAccessPathNil and - ap = TRevPartialNil() and - config = mid.getConfiguration() - or - revPartialPathReadStep(mid, _, _, node, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - config = mid.getConfiguration() - or - exists(RevPartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - apConsRev(ap, c, ap0, config) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, ap, config) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, - RevPartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode, mid.getConfiguration()) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev( - RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config - ) { - exists(PartialPathNodeRev mid | - revPartialPathReadStep(mid, ap1, c, _, ap2) and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config - ) { - exists(NodeEx midNode, TypedContent tc | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - store(node, tc, midNode, _, config) and - ap.getHead() = c and - config = mid.getConfiguration() and - tc.getContent() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - DataFlowCall call, RevPartialAccessPath ap, Configuration config - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(ap) and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, - Configuration config - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p | - mid.getNodeEx() = p and - p.getPosition() = pos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - ap = mid.getAp() and - config = mid.getConfiguration() - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap, - Configuration config - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 | - revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and - revPartialPathFlowsThrough(pos, sc1, sc2, ap, config) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config - ) { - exists(DataFlowCall call, int pos | - revPartialPathThroughCallable0(call, mid, pos, ap, config) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } -} - -import FlowExploration - -private predicate partialFlow( - PartialPathNode source, PartialPathNode node, Configuration configuration -) { - source.getConfiguration() = configuration and - source.isFwdSource() and - node = source.getASuccessor+() -} - -private predicate revPartialFlow( - PartialPathNode node, PartialPathNode sink, Configuration configuration -) { - sink.getConfiguration() = configuration and - sink.isRevSink() and - node.getASuccessor+() = sink -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll deleted file mode 100644 index c28ceabb4..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll +++ /dev/null @@ -1,1331 +0,0 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { - p.isParameterOf(viableCallable(call), i) - } - - private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), i) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(int i | - viableParamNonLambda(call, i, p) and - arg.argumentOf(call, i) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(int i | - viableParamLambda(call, i, p) and - arg.argumentOf(call, i) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda( - DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind - ) { - result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - private predicate viableReturnPosOutLambda( - DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, lastCall, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and - toReturn = false and - toJump = true and - lastCall = TDataFlowCallNone() - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, _, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, int pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - read(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) } - - cached - predicate argumentNode(Node n, DataFlowCall call, int pos) { - n.(ArgumentNode).argumentOf(call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`. - * The instance parameter is considered to have index `-1`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParamNode p) { - p.isParameterOf(viableCallableExt(call), i) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(int i | - viableParam(call, i, p) and - arg.argumentOf(call, i) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - read(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - */ - predicate getterStep(ArgNode arg, Content c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - pragma[nomagic] - private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - private predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - read(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - cached - predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `f`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { - store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } - - cached - newtype TAccessPathFront = - TFrontNil(DataFlowType t) or - TFrontHead(TypedContent tc) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode extends Node { - CastingNode() { castingNode(this) } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, Content c, Node n2, DataFlowType content -) { - read(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - Content getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode extends Node { - ParamNode() { parameterNode(this, _, _) } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * (zero-based) position. - */ - predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode extends Node { - ArgNode() { argumentNode(this, _, _) } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt extends Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt extends Node { - OutNodeExt() { outNodeExt(this) } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private int pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - int getPosition() { result = pos } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** Content tagged with the type of a containing object. */ -class TypedContent extends MkTypedContent { - private Content c; - private DataFlowType t; - - TypedContent() { this = MkTypedContent(c, t) } - - /** Gets the content. */ - Content getContent() { result = c } - - /** Gets the container type. */ - DataFlowType getContainerType() { result = t } - - /** Gets a textual representation of this content. */ - string toString() { result = c.toString() } - - /** - * Holds if access paths with this `TypedContent` at their head always should - * be tracked at high precision. This disables adaptive access path precision - * for such access paths. - */ - predicate forceHighPrecision() { forceHighPrecision(c) } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract DataFlowType getType(); - - abstract boolean toBoolNonEmpty(); - - TypedContent getHead() { this = TFrontHead(result) } - - predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - private DataFlowType t; - - AccessPathFrontNil() { this = TFrontNil(t) } - - override string toString() { result = ppReprType(t) } - - override DataFlowType getType() { result = t } - - override boolean toBoolNonEmpty() { result = false } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private TypedContent tc; - - AccessPathFrontHead() { this = TFrontHead(tc) } - - override string toString() { result = tc.toString() } - - override DataFlowType getType() { result = tc.getContainerType() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll deleted file mode 100644 index ee044c5e4..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Provides Go-specific definitions for use in the data flow library. - */ -module Private { - import DataFlowPrivate - import DataFlowDispatch -} - -module Public { - import DataFlowUtil -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll deleted file mode 100644 index 80c94cafd..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ /dev/null @@ -1,1161 +0,0 @@ -private import go -private import semmle.go.dataflow.FunctionInputsAndOutputs -private import semmle.go.dataflow.FlowSummary -private import DataFlowPrivate -private import FlowSummaryImpl as FlowSummaryImpl -private import semmle.go.dataflow.ExternalFlow - -cached -private newtype TNode = - MkInstructionNode(IR::Instruction insn) or - MkSsaNode(SsaDefinition ssa) or - MkGlobalFunctionNode(Function f) or - MkSummarizedParameterNode(DataFlowCallable c, int i) { - not exists(c.getFuncDef()) and - c instanceof SummarizedCallable and - ( - i in [0 .. c.getType().getNumParameter() - 1] - or - c.asFunction() instanceof Method and i = -1 - ) - } or - MkSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { - FlowSummaryImpl::Private::summaryNodeRange(c, state) - } - -/** Nodes intended for only use inside the data-flow libraries. */ -module Private { - /** Gets the callable in which this node occurs. */ - DataFlowCallable nodeGetEnclosingCallable(Node n) { - result.asCallable() = n.getEnclosingCallable() - or - not exists(n.getEnclosingCallable()) and result.asFileScope() = n.getFile() - } - - /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ - predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { - p.isParameterOf(c.asCallable(), pos) - } - - /** A data flow node that represents returning a value from a function. */ - class ReturnNode extends Node { - ReturnKind kind; - - ReturnNode() { - this.(Public::ResultNode).getIndex() = kind.getIndex() - or - this.(SummaryNode).isReturn(kind) - } - - /** Gets the kind of this returned value. */ - ReturnKind getKind() { result = kind } - } - - /** A data flow node that represents the output of a call. */ - class OutNode extends Node { - DataFlow::CallNode call; - int i; - - OutNode() { this = call.getResult(i) } - - /** Gets the underlying call. */ - DataFlowCall getCall() { result = call.asExpr() } - } - - /** - * A data-flow node used to model flow summaries. - */ - class SummaryNode extends Node, MkSummaryInternalNode { - private SummarizedCallable c; - private FlowSummaryImpl::Private::SummaryNodeState state; - - SummaryNode() { this = MkSummaryInternalNode(c, state) } - - override predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) { - c.hasLocationInfo(fp, sl, sc, el, ec) - } - - override string toString() { result = "[summary] " + state + " in " + c } - - /** Holds if this summary node is the `i`th argument of `call`. */ - predicate isArgumentOf(DataFlowCall call, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(call, this, i) - } - - /** Holds if this summary node is a return node. */ - predicate isReturn(ReturnKind kind) { FlowSummaryImpl::Private::summaryReturnNode(this, kind) } - - /** Holds if this summary node is an out node for `call`. */ - predicate isOut(DataFlowCall call) { FlowSummaryImpl::Private::summaryOutNode(call, this, _) } - } - - /** Gets the summary node corresponding to the callable `c` and state `state`. */ - SummaryNode getSummaryNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { - result = MkSummaryInternalNode(c, state) - } -} - -/** Nodes intended for use outside the data-flow libraries. */ -module Public { - /** - * A node in a data flow graph. - * - * A node can be either an IR instruction or an SSA definition. - * Such nodes are created with `DataFlow::instructionNode` - * and `DataFlow::ssaNode` respectively. - */ - class Node extends TNode { - /** Gets the function to which this node belongs. */ - ControlFlow::Root getRoot() { none() } // overridden in subclasses - - /** INTERNAL: Use `getRoot()` instead. */ - Callable getEnclosingCallable() { - result.getFuncDef() = this.getRoot() - or - exists(DataFlowCallable dfc | result = dfc.asCallable() | - this = MkSummarizedParameterNode(dfc, _) - or - this = MkSummaryInternalNode(dfc, _) - ) - } - - /** Gets the type of this node. */ - Type getType() { none() } // overridden in subclasses - - /** Gets the expression corresponding to this node, if any. */ - Expr asExpr() { none() } // overridden in subclasses - - /** Gets the parameter corresponding to this node, if any. */ - Parameter asParameter() { none() } // overridden in subclasses - - /** Gets the IR instruction corresponding to this node, if any. */ - IR::Instruction asInstruction() { none() } // overridden in subclasses - - /** Gets a textual representation of the kind of this data-flow node. */ - string getNodeKind() { none() } // overridden in subclasses - - /** Gets the basic block to which this data-flow node belongs, if any. */ - BasicBlock getBasicBlock() { result = this.asInstruction().getBasicBlock() } - - /** Gets a textual representation of this element. */ - string toString() { result = "data-flow node" } // overridden in subclasses - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - - /** Gets the file in which this node appears. */ - File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } - - /** Gets the start line of the location of this node. */ - int getStartLine() { this.hasLocationInfo(_, result, _, _, _) } - - /** Gets the start column of the location of this node. */ - int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) } - - /** Gets the end line of the location of this node. */ - int getEndLine() { this.hasLocationInfo(_, _, _, result, _) } - - /** Gets the end column of the location of this node. */ - int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) } - - /** - * Gets an upper bound on the type of this node. - */ - Type getTypeBound() { result = this.getType() } - - /** Gets the floating-point value this data-flow node contains, if any. */ - float getFloatValue() { result = this.asExpr().getFloatValue() } - - /** - * Gets the integer value this data-flow node contains, if any. - * - * Note that this does not have a result if the value is too large to fit in a - * 32-bit signed integer type. - */ - int getIntValue() { result = this.asInstruction().getIntValue() } - - /** Gets either `getFloatValue` or `getIntValue`. */ - float getNumericValue() { result = this.asInstruction().getNumericValue() } - - /** - * Holds if the complex value this data-flow node contains has real part `real` and imaginary - * part `imag`. - */ - predicate hasComplexValue(float real, float imag) { - this.asInstruction().hasComplexValue(real, imag) - } - - /** Gets the string value this data-flow node contains, if any. */ - string getStringValue() { result = this.asInstruction().getStringValue() } - - /** - * Gets the string representation of the exact value this data-flow node - * contains, if any. - * - * For example, for the constant 3.141592653589793238462, this will - * result in 1570796326794896619231/500000000000000000000 - */ - string getExactValue() { result = this.asInstruction().getExactValue() } - - /** Gets the Boolean value this data-flow node contains, if any. */ - boolean getBoolValue() { result = this.asInstruction().getBoolValue() } - - /** Holds if the value of this data-flow node is known at compile time. */ - predicate isConst() { this.asInstruction().isConst() } - - /** - * Holds if the result of this instruction is known at compile time, and is guaranteed not to - * depend on the platform where it is evaluated. - */ - predicate isPlatformIndependentConstant() { - this.asInstruction().isPlatformIndependentConstant() - } - - /** - * Gets a data-flow node to which data may flow from this node in one (intra-procedural) step. - */ - Node getASuccessor() { DataFlow::localFlowStep(this, result) } - - /** - * Gets a data-flow node from which data may flow to this node in one (intra-procedural) step. - */ - Node getAPredecessor() { this = result.getASuccessor() } - } - - /** - * An IR instruction, viewed as a node in a data flow graph. - */ - class InstructionNode extends Node, MkInstructionNode { - IR::Instruction insn; - - InstructionNode() { this = MkInstructionNode(insn) } - - override IR::Instruction asInstruction() { result = insn } - - override ControlFlow::Root getRoot() { result = insn.getRoot() } - - override Type getType() { result = insn.getResultType() } - - override string getNodeKind() { result = insn.getInsnKind() } - - override string toString() { result = insn.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - insn.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * An expression, viewed as a node in a data flow graph. - */ - class ExprNode extends InstructionNode { - override IR::EvalInstruction insn; - Expr expr; - - ExprNode() { expr = insn.getExpr() } - - override Expr asExpr() { result = expr } - - /** Gets the underlying expression this node corresponds to. */ - Expr getExpr() { result = expr } - } - - /** - * An SSA variable, viewed as a node in a data flow graph. - */ - class SsaNode extends Node, MkSsaNode { - SsaDefinition ssa; - - SsaNode() { this = MkSsaNode(ssa) } - - /** Gets the node whose value is stored in this SSA variable, if any. */ - Node getInit() { result = DataFlow::instructionNode(ssa.(SsaExplicitDefinition).getRhs()) } - - /** Gets a use of this SSA variable. */ - InstructionNode getAUse() { result = DataFlow::instructionNode(ssa.getVariable().getAUse()) } - - /** Gets the program variable corresponding to this SSA variable. */ - SsaSourceVariable getSourceVariable() { result = ssa.getSourceVariable() } - - /** Gets the unique definition of this SSA variable. */ - SsaDefinition getDefinition() { result = ssa } - - override ControlFlow::Root getRoot() { result = ssa.getRoot() } - - override Type getType() { result = ssa.getSourceVariable().getType() } - - override string getNodeKind() { result = "SSA variable " + ssa.getSourceVariable().getName() } - - override string toString() { result = ssa.toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - ssa.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private module FunctionNode { - /** A function, viewed as a node in a data flow graph. */ - abstract class Range extends Node { - /** Gets the `i`th parameter of this function. */ - abstract ParameterNode getParameter(int i); - - /** Gets the name of this function, if it has one. */ - abstract string getName(); - - /** - * Gets the dataflow node holding the value of the receiver, if any. - */ - abstract ReceiverNode getReceiver(); - - /** - * Gets a value returned by the given function via a return statement or an assignment to a - * result variable. - */ - abstract ResultNode getAResult(); - - /** - * Gets the function entity this node corresponds to. - * - * Note that this predicate has no result for function literals. - */ - Function getFunction() { none() } - } - } - - /** A function, viewed as a node in a data flow graph. */ - class FunctionNode extends Node { - FunctionNode::Range self; - - FunctionNode() { this = self } - - /** Gets the `i`th parameter of this function. */ - ParameterNode getParameter(int i) { result = self.getParameter(i) } - - /** Gets a parameter of this function. */ - ParameterNode getAParameter() { result = this.getParameter(_) } - - /** Gets the number of parameters declared on this function. */ - int getNumParameter() { result = count(this.getAParameter()) } - - /** Gets the name of this function, if it has one. */ - string getName() { result = self.getName() } - - /** - * Gets the dataflow node holding the value of the receiver, if any. - */ - ReceiverNode getReceiver() { result = self.getReceiver() } - - /** - * Gets a value returned by the given function via a return statement or an assignment to a - * result variable. - */ - ResultNode getAResult() { result = self.getAResult() } - - /** - * Gets the data-flow node corresponding to the `i`th result of this function. - */ - ResultNode getResult(int i) { result = this.getAResult() and result.getIndex() = i } - - /** - * Gets the function entity this node corresponds to. - * - * Note that this predicate has no result for function literals. - */ - Function getFunction() { result = self.getFunction() } - } - - /** A representation of a function that is declared in the module scope. */ - class GlobalFunctionNode extends FunctionNode::Range, MkGlobalFunctionNode { - Function func; - - GlobalFunctionNode() { this = MkGlobalFunctionNode(func) } - - override ParameterNode getParameter(int i) { - result = DataFlow::parameterNode(func.getParameter(i)) - } - - override string getName() { result = func.getName() } - - override Function getFunction() { result = func } - - override ReceiverNode getReceiver() { - result = DataFlow::receiverNode(func.(Method).getReceiver()) - } - - override string getNodeKind() { result = "function " + func.getName() } - - override string toString() { result = "function " + func.getName() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - func.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - override ResultNode getAResult() { - result.getRoot() = this.getFunction().(DeclaredFunction).getFuncDecl() - } - } - - /** A representation of the function that is defined by a function literal. */ - class FuncLitNode extends FunctionNode::Range, ExprNode { - override FuncLit expr; - - override ParameterNode getParameter(int i) { - result = DataFlow::parameterNode(expr.getParameter(i)) - } - - override string getName() { none() } - - override ReceiverNode getReceiver() { none() } - - override string toString() { result = "function literal" } - - override ResultNode getAResult() { result.getRoot() = this.getExpr() } - } - - /** - * Gets a possible target of call `cn`.class - * - * This is written explicitly like this instead of using `getCalleeNode().getAPredecessor*()` - * or `result.getASuccessor*() = cn.getCalleeNode()` because the explicit form inhibits the - * optimizer from combining this with other uses of `getASuccessor*()`, which can lead to - * recursion through a magic side-condition if those other users call `getACallee()` and thus - * pointless recomputation of `getACallee()` each recursive iteration. - */ - private DataFlow::Node getACalleeSource(DataFlow::CallNode cn) { - result = cn.getCalleeNode() or - basicLocalFlowStep(result, getACalleeSource(cn)) or - result.asExpr() = getACalleeSource(cn).asExpr().(GenericFunctionInstantiationExpr).getBase() - } - - /** A data flow node that represents a call. */ - class CallNode extends ExprNode { - override CallExpr expr; - - /** Gets the declared target of this call */ - Function getTarget() { result = expr.getTarget() } - - private DataFlow::Node getACalleeSource() { result = getACalleeSource(this) } - - /** - * Gets the definition of a possible target of this call. - * - * For non-virtual calls, there is at most one possible call target (but there may be none if the - * target has no declaration). - * - * For virtual calls, we look up possible targets in all types that implement the receiver - * interface type. - */ - Callable getACalleeIncludingExternals() { - result.asFunction() = this.getTarget() - or - exists(DataFlow::Node calleeSource | calleeSource = this.getACalleeSource() | - result.asFuncLit() = calleeSource.asExpr() - or - calleeSource = result.asFunction().getARead() - or - exists(Method declared, Method actual | - calleeSource = declared.getARead() and - actual.implements(declared) and - result.asFunction() = actual - ) - ) - } - - /** - * As `getACalleeIncludingExternals`, except excluding external functions (those for which - * we lack a definition, such as standard library functions). - */ - FuncDef getACallee() { result = this.getACalleeIncludingExternals().getFuncDef() } - - /** - * Gets the name of the function, method or variable that is being called. - * - * Note that if we are calling a variable then this gets the variable name. - * It does not attempt to get the name of the function or method that is - * assigned to the variable. To do that, use - * `getACalleeIncludingExternals().asFunction().getName()`. - */ - string getCalleeName() { result = expr.getCalleeName() } - - /** Gets the data flow node specifying the function to be called. */ - Node getCalleeNode() { result = DataFlow::exprNode(expr.getCalleeExpr()) } - - /** Gets the underlying call. */ - CallExpr getCall() { result = this.getExpr() } - - /** - * Gets the data flow node corresponding to the `i`th argument of this call. - * - * Note that the first argument in calls to the built-in function `make` is a type, which is - * not a data-flow node. It is skipped for the purposes of this predicate, so the (syntactically) - * second argument becomes the first argument in terms of data flow. - * - * For calls of the form `f(g())` where `g` has multiple results, the arguments of the call to - * `i` are the (implicit) element extraction nodes for the call to `g`. - */ - Node getArgument(int i) { - if expr.getArgument(0).getType() instanceof TupleType - then result = DataFlow::extractTupleElement(DataFlow::exprNode(expr.getArgument(0)), i) - else - result = - rank[i + 1](Expr arg, int j | - arg = expr.getArgument(j) - | - DataFlow::exprNode(arg) order by j - ) - } - - /** Gets the data flow node corresponding to an argument of this call. */ - Node getAnArgument() { result = this.getArgument(_) } - - /** Gets the number of arguments of this call, if it can be determined. */ - int getNumArgument() { result = count(this.getAnArgument()) } - - /** Gets a function passed as the `i`th argument of this call. */ - FunctionNode getCallback(int i) { result.getASuccessor*() = this.getArgument(i) } - - /** - * Gets the data-flow node corresponding to the `i`th result of this call. - * - * If there is a single result then it is considered to be the 0th result. - */ - Node getResult(int i) { - i = 0 and result = this.getResult() - or - result = DataFlow::extractTupleElement(this, i) - } - - /** - * Gets the data-flow node corresponding to the result of this call. - * - * Note that this predicate is not defined for calls with multiple results; use the one-argument - * variant `getResult(i)` for such calls. - */ - Node getResult() { not this.getType() instanceof TupleType and result = this } - - /** Gets a result of this call. */ - Node getAResult() { result = this.getResult(_) } - - /** Gets the data flow node corresponding to the receiver of this call, if any. */ - Node getReceiver() { result = this.getACalleeSource().(MethodReadNode).getReceiver() } - - /** Holds if this call has an ellipsis after its last argument. */ - predicate hasEllipsis() { expr.hasEllipsis() } - } - - /** A data flow node that represents a call to a method. */ - class MethodCallNode extends CallNode { - MethodCallNode() { expr.getTarget() instanceof Method } - - override Method getTarget() { result = expr.getTarget() } - - override MethodDecl getACallee() { result = super.getACallee() } - } - - /** A representation of a parameter initialization. */ - abstract class ParameterNode extends DataFlow::Node { - /** Holds if this node initializes the `i`th parameter of `c`. */ - abstract predicate isParameterOf(Callable c, int i); - } - - /** - * A summary node which represents a parameter in a function which doesn't - * already have a parameter nodes. - */ - class SummarizedParameterNode extends ParameterNode, MkSummarizedParameterNode { - Callable c; - int i; - - SummarizedParameterNode() { - this = MkSummarizedParameterNode(any(DataFlowCallable dfc | c = dfc.asCallable()), i) - } - - // There are no AST representations of summarized parameter nodes - override ControlFlow::Root getRoot() { none() } - - override string getNodeKind() { result = "external parameter node" } - - override Type getType() { - result = c.getType().getParameterType(i) - or - i = -1 and result = c.asFunction().(Method).getReceiverType() - } - - override predicate isParameterOf(Callable call, int idx) { c = call and i = idx } - - override string toString() { result = "parameter " + i + " of " + c.toString() } - - override predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) { - c.hasLocationInfo(fp, sl, sc, el, ec) - } - } - - /** A representation of a parameter initialization, defined in source via an SSA node. */ - class SsaParameterNode extends ParameterNode, SsaNode { - override SsaExplicitDefinition ssa; - Parameter parm; - - SsaParameterNode() { ssa.getInstruction() = IR::initParamInstruction(parm) } - - /** Gets the parameter this node initializes. */ - override Parameter asParameter() { result = parm } - - override predicate isParameterOf(Callable c, int i) { parm.isParameterOf(c.getFuncDef(), i) } - } - - /** A representation of a receiver initialization. */ - class ReceiverNode extends SsaParameterNode { - override ReceiverVariable parm; - - /** Gets the receiver variable this node initializes. */ - ReceiverVariable asReceiverVariable() { result = parm } - - /** Holds if this node initializes the receiver variable of `m`. */ - predicate isReceiverOf(MethodDecl m) { parm.isReceiverOf(m) } - } - - private Node getADirectlyWrittenNode() { - exists(Write w | w.writesField(result, _, _) or w.writesElement(result, _, _)) - } - - private DataFlow::Node getAccessPathPredecessor(DataFlow::Node node) { - result = node.(PointerDereferenceNode).getOperand() - or - result = node.(ComponentReadNode).getBase() - } - - private Node getAWrittenNode() { result = getAccessPathPredecessor*(getADirectlyWrittenNode()) } - - /** - * Holds if `tp` is a type that may (directly or indirectly) reference a memory location. - * - * If a value with a mutable type is passed to a function, the function could potentially - * mutate it or something it points to. - */ - predicate mutableType(Type tp) { - exists(Type underlying | underlying = tp.getUnderlyingType() | - not underlying instanceof BoolType and - not underlying instanceof NumericType and - not underlying instanceof StringType and - not underlying instanceof LiteralType - ) - } - - /** - * A node associated with an object after an operation that might have - * changed its state. - * - * This can be either the argument to a callable after the callable returns - * (which might have mutated the argument), or the qualifier of a field after - * an update to the field. - * - * Nodes corresponding to AST elements, for example `ExprNode`, usually refer - * to the value before the update with the exception of `ClassInstanceExpr`, - * which represents the value after the constructor has run. - */ - abstract class PostUpdateNode extends Node { - /** - * Gets the node before the state update. - */ - abstract Node getPreUpdateNode(); - } - - private class DefaultPostUpdateNode extends PostUpdateNode { - Node preupd; - - DefaultPostUpdateNode() { - ( - preupd instanceof AddressOperationNode - or - preupd = any(AddressOperationNode addr).getOperand() - or - preupd = any(PointerDereferenceNode deref).getOperand() - or - preupd = getAWrittenNode() - or - preupd instanceof ArgumentNode and - mutableType(preupd.getType()) - ) and - ( - preupd = this.(SsaNode).getAUse() - or - preupd = this and - not basicLocalFlowStep(_, this) - ) - } - - override Node getPreUpdateNode() { result = preupd } - } - - /** - * A data-flow node that occurs as an argument in a call, including receiver arguments. - */ - class ArgumentNode extends Node { - CallNode c; - int i; - - ArgumentNode() { this = getArgument(c, i) } - - /** - * Holds if this argument occurs at the given position in the given call. - * - * The receiver argument is considered to have index `-1`. - * - * Note that we currently do not track receiver arguments into calls to interface methods. - */ - predicate argumentOf(CallExpr call, int pos) { - call = c.asExpr() and - pos = i and - ( - i != -1 - or - exists(c.(MethodCallNode).getTarget().getBody()) - or - hasExternalSpecification(c.(DataFlow::MethodCallNode).getTarget()) - ) - } - - /** - * Gets the `CallNode` this is an argument to. - */ - CallNode getCall() { result = c } - } - - /** - * A node whose value is returned as a result from a function. - * - * This can either be a node corresponding to an expression in a return statement, - * or a node representing the current value of a named result variable at the exit - * of the function. - */ - class ResultNode extends InstructionNode { - FuncDef fd; - int i; - - ResultNode() { - exists(IR::ReturnInstruction ret | ret.getRoot() = fd | insn = ret.getResult(i)) - or - insn.(IR::ReadResultInstruction).reads(fd.getResultVar(i)) - } - - /** Gets the index of this result among all results of the function. */ - int getIndex() { result = i } - } - - /** - * A data-flow node that reads the value of a variable, constant, field or array element, - * or refers to a function. - */ - class ReadNode extends InstructionNode { - override IR::ReadInstruction insn; - - /** - * Holds if this data-flow node evaluates to value of `v`, which is a value entity, that is, a - * constant, variable, field, function, or method. - */ - predicate reads(ValueEntity v) { insn.reads(v) } - - /** - * Holds if this data-flow node reads the value of SSA variable `v`. - */ - predicate readsSsaVariable(SsaVariable v) { insn = v.getAUse() } - - /** - * Holds if this data-flow node reads the value of field `f` on the value of `base` or its - * implicit dereference. - * - * For example, for the field read `x.width`, `base` is either the data-flow node corresponding - * to `x` or (if `x` is a pointer) the data-flow node corresponding to the implicit dereference - * `*x`, and `f` is the field referenced by `width`. - */ - predicate readsField(Node base, Field f) { - insn.readsField(base.asInstruction(), f) - or - insn.readsField(IR::implicitDerefInstruction(base.asExpr()), f) - } - - /** - * Holds if this data-flow node reads the value of field `package.type.field` on the value of `base` or its - * implicit dereference. - * - * For example, for the field read `x.width`, `base` is either the data-flow node corresponding - * to `x` or (if `x` is a pointer) the data-flow node corresponding to the implicit dereference - * `*x`, and `x` has the type `package.type`. - */ - predicate readsField(Node base, string package, string type, string field) { - exists(Field f | f.hasQualifiedName(package, type, field) | this.readsField(base, f)) - } - - /** - * Holds if this data-flow node looks up method `m` on the value of `receiver` or its implicit - * dereference. - * - * For example, for the method read `x.area`, `receiver` is either the data-flow node corresponding - * to `x` or (if `x` is a pointer) the data-flow node corresponding to the implicit dereference - * `*x`, and `m` is the method referenced by `area`. - */ - predicate readsMethod(Node receiver, Method m) { - insn.readsMethod(receiver.asInstruction(), m) - or - insn.readsMethod(IR::implicitDerefInstruction(receiver.asExpr()), m) - } - - /** - * Holds if this data-flow node looks up method `package.type.name` on the value of `receiver` - * or its implicit dereference. - * - * For example, for the method read `x.name`, `receiver` is either the data-flow node corresponding - * to `x` or (if `x` is a pointer) the data-flow node corresponding to the implicit dereference - * `*x`, and `package.type` is a type of `x` that defines a method named `name`. - */ - predicate readsMethod(Node receiver, string package, string type, string name) { - exists(Method m | m.hasQualifiedName(package, type, name) | this.readsMethod(receiver, m)) - } - - /** - * Holds if this data-flow node reads the value of element `index` on the value of `base` or its - * implicit dereference. - * - * For example, for the element read `xs[i]`, `base` is either the data-flow node corresponding - * to `xs` or (if `xs` is a pointer) the data-flow node corresponding to the implicit dereference - * `*xs`, and `index` is the data-flow node corresponding to `i`. - */ - predicate readsElement(Node base, Node index) { - insn.readsElement(base.asInstruction(), index.asInstruction()) - or - insn.readsElement(IR::implicitDerefInstruction(base.asExpr()), index.asInstruction()) - } - } - - /** - * A data-flow node that reads the value of a field from a struct, or an element from an array, slice, map or string. - */ - class ComponentReadNode extends ReadNode { - override IR::ComponentReadInstruction insn; - - /** Gets the data-flow node representing the base from which the field or element is read. */ - Node getBase() { result = DataFlow::instructionNode(insn.getBase()) } - } - - /** - * A data-flow node that reads an element of an array, map, slice or string. - */ - class ElementReadNode extends ComponentReadNode { - override IR::ElementReadInstruction insn; - - /** Gets the data-flow node representing the index of the element being read. */ - Node getIndex() { result = DataFlow::instructionNode(insn.getIndex()) } - - /** Holds if this data-flow node reads element `index` of `base`. */ - predicate reads(Node base, Node index) { this.readsElement(base, index) } - } - - /** - * A data-flow node that extracts a substring or slice from a string, array, pointer to array, - * or slice. - */ - class SliceNode extends InstructionNode { - override IR::SliceInstruction insn; - - /** Gets the base of this slice node. */ - Node getBase() { result = DataFlow::instructionNode(insn.getBase()) } - - /** Gets the lower bound of this slice node. */ - Node getLow() { result = DataFlow::instructionNode(insn.getLow()) } - - /** Gets the upper bound of this slice node. */ - Node getHigh() { result = DataFlow::instructionNode(insn.getHigh()) } - - /** Gets the maximum of this slice node. */ - Node getMax() { result = DataFlow::instructionNode(insn.getMax()) } - } - - /** - * A data-flow node corresponding to an expression with a binary operator. - */ - class BinaryOperationNode extends Node { - Node left; - Node right; - string op; - - BinaryOperationNode() { - exists(BinaryExpr bin | bin = this.asExpr() | - left = DataFlow::exprNode(bin.getLeftOperand()) and - right = DataFlow::exprNode(bin.getRightOperand()) and - op = bin.getOperator() - ) - or - exists(IR::EvalCompoundAssignRhsInstruction rhs, CompoundAssignStmt assgn, string o | - rhs = this.asInstruction() and assgn = rhs.getAssignment() and o = assgn.getOperator() - | - left = DataFlow::exprNode(assgn.getLhs()) and - right = DataFlow::exprNode(assgn.getRhs()) and - op = o.substring(0, o.length() - 1) - ) - or - exists(IR::EvalIncDecRhsInstruction rhs, IncDecStmt ids | - rhs = this.asInstruction() and ids = rhs.getStmt() - | - left = DataFlow::exprNode(ids.getOperand()) and - right = - DataFlow::instructionNode(any(IR::EvalImplicitOneInstruction one | one.getStmt() = ids)) and - op = ids.getOperator().charAt(0) - ) - } - - /** Holds if this operation may have observable side effects. */ - predicate mayHaveSideEffects() { this.asExpr().mayHaveOwnSideEffects() } - - /** Gets the left operand of this operation. */ - Node getLeftOperand() { result = left } - - /** Gets the right operand of this operation. */ - Node getRightOperand() { result = right } - - /** Gets an operand of this operation. */ - Node getAnOperand() { result = left or result = right } - - /** Gets the operator of this operation. */ - string getOperator() { result = op } - - /** Holds if `x` and `y` are the operands of this operation, in either order. */ - predicate hasOperands(Node x, Node y) { - x = this.getAnOperand() and - y = this.getAnOperand() and - x != y - } - } - - /** - * A data-flow node corresponding to an expression with a unary operator. - */ - class UnaryOperationNode extends InstructionNode { - UnaryOperationNode() { - this.asExpr() instanceof UnaryExpr - or - this.asExpr() instanceof StarExpr - or - insn instanceof IR::EvalImplicitDerefInstruction - } - - /** Holds if this operation may have observable side effects. */ - predicate mayHaveSideEffects() { - this.asExpr().mayHaveOwnSideEffects() - or - insn instanceof IR::EvalImplicitDerefInstruction - } - - /** Gets the operand of this operation. */ - Node getOperand() { - result = DataFlow::exprNode(this.asExpr().(UnaryExpr).getOperand()) - or - result = DataFlow::exprNode(this.asExpr().(StarExpr).getBase()) - or - result = DataFlow::exprNode(insn.(IR::EvalImplicitDerefInstruction).getOperand()) - } - - /** Gets the operator of this operation. */ - string getOperator() { - result = this.asExpr().(UnaryExpr).getOperator() - or - this.asExpr() instanceof StarExpr and - result = "*" - or - insn instanceof IR::EvalImplicitDerefInstruction and - result = "*" - } - } - - /** - * A data-flow node that dereferences a pointer. - */ - class PointerDereferenceNode extends UnaryOperationNode { - PointerDereferenceNode() { - this.asExpr() instanceof StarExpr - or - this.asExpr() instanceof DerefExpr - or - insn instanceof IR::EvalImplicitDerefInstruction - } - } - - /** - * A data-flow node that takes the address of a memory location. - */ - class AddressOperationNode extends UnaryOperationNode, ExprNode { - override AddressExpr expr; - } - - /** - * A data-flow node that reads the value of a field. - */ - class FieldReadNode extends ComponentReadNode { - override IR::FieldReadInstruction insn; - - /** Gets the field this node reads. */ - Field getField() { result = insn.getField() } - - /** Gets the name of the field this node reads. */ - string getFieldName() { result = this.getField().getName() } - } - - /** - * A data-flow node that refers to a method. - */ - class MethodReadNode extends ReadNode { - override IR::MethodReadInstruction insn; - - /** Gets the receiver node on which the method is referenced. */ - Node getReceiver() { result = DataFlow::instructionNode(insn.getReceiver()) } - - /** Gets the method this node refers to. */ - Method getMethod() { result = insn.getMethod() } - - /** Gets the name of the method this node refers to. */ - string getMethodName() { result = this.getMethod().getName() } - } - - /** - * A data-flow node performing a relational comparison using `<`, `<=`, `>` or `>=`. - */ - class RelationalComparisonNode extends BinaryOperationNode, ExprNode { - override RelationalComparisonExpr expr; - - /** Holds if this comparison evaluates to `outcome` iff `lesser <= greater + bias`. */ - predicate leq(boolean outcome, Node lesser, Node greater, int bias) { - outcome = true and - lesser = DataFlow::exprNode(expr.getLesserOperand()) and - greater = DataFlow::exprNode(expr.getGreaterOperand()) and - (if expr.isStrict() then bias = -1 else bias = 0) - or - outcome = false and - lesser = DataFlow::exprNode(expr.getGreaterOperand()) and - greater = DataFlow::exprNode(expr.getLesserOperand()) and - (if expr.isStrict() then bias = 0 else bias = -1) - } - } - - /** - * A data-flow node performing an equality test using `==` or `!=`. - */ - class EqualityTestNode extends BinaryOperationNode, ExprNode { - override EqualityTestExpr expr; - - /** Holds if this comparison evaluates to `outcome` iff `lhs == rhs`. */ - predicate eq(boolean outcome, Node lhs, Node rhs) { - outcome = expr.getPolarity() and - expr.hasOperands(lhs.asExpr(), rhs.asExpr()) - } - - /** Gets the polarity of this equality test, that is, `true` for `==` and `false` for `!=`. */ - boolean getPolarity() { result = expr.getPolarity() } - } - - /** - * A data-flow node performing a type cast using either a type conversion - * or an assertion. - */ - class TypeCastNode extends ExprNode { - TypeCastNode() { - expr instanceof TypeAssertExpr - or - expr instanceof ConversionExpr - } - - /** - * Gets the type being converted to. Note this differs from `this.getType()` for - * `TypeAssertExpr`s that return a (result, ok) tuple. - */ - Type getResultType() { - if this.getType() instanceof TupleType - then result = this.getType().(TupleType).getComponentType(0) - else result = this.getType() - } - - /** Gets the operand of the type cast. */ - DataFlow::Node getOperand() { - result.asExpr() = expr.(TypeAssertExpr).getExpr() - or - result.asExpr() = expr.(ConversionExpr).getOperand() - } - } - - /** - * A data-flow node representing an element of an array, map, slice or string defined from `range` statement. - * - * Example: in `_, x := range y { ... }`, this represents the `Node` that extracts the element from the - * range statement, which will flow to `x`. - */ - class RangeElementNode extends Node { - DataFlow::Node base; - IR::ExtractTupleElementInstruction extract; - - RangeElementNode() { - this.asInstruction() = extract and - extract.extractsElement(_, 1) and - extract.getBase().(IR::GetNextEntryInstruction).getDomain() = base.asInstruction() - } - - /** Gets the data-flow node representing the base from which the element is read. */ - DataFlow::Node getBase() { result = base } - } - - /** - * A data-flow node representing an index of an array, map, slice or string defined from `range` statement. - * - * Example: in `i, _ := range y { ... }`, this represents the `Node` that extracts the index from the - * range statement, which will flow to `i`. - */ - class RangeIndexNode extends Node { - DataFlow::Node base; - - RangeIndexNode() { - // when there is a comma, as in `i, x := range y { ... }` - exists(IR::ExtractTupleElementInstruction extract | - this.asInstruction() = extract and - extract.extractsElement(_, 0) and - extract.getBase().(IR::GetNextEntryInstruction).getDomain() = base.asInstruction() - ) - or - // when there is no comma, as in `i := range y { ... }` - not exists(IR::ExtractTupleElementInstruction extract | - extract.getBase() = this.asInstruction() - ) and - base.asInstruction() = this.asInstruction().(IR::GetNextEntryInstruction).getDomain() - } - - /** Gets the data-flow node representing the base from which the element is read. */ - DataFlow::Node getBase() { result = base } - } -} - -private import Private -private import Public - -class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { - private Node pre; - - SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) } - - override Node getPreUpdateNode() { result = pre } -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll deleted file mode 100644 index 9b8cfb194..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ /dev/null @@ -1,348 +0,0 @@ -private import go -private import DataFlowUtil -private import DataFlowImplCommon -private import ContainerFlow -private import FlowSummaryImpl as FlowSummaryImpl -import DataFlowNodes::Private - -private newtype TReturnKind = - MkReturnKind(int i) { exists(SignatureType st | exists(st.getResultType(i))) } - -ReturnKind getReturnKind(int i) { result = MkReturnKind(i) } - -/** - * A return kind. A return kind describes how a value can be returned - * from a callable. For Go, this is either a return of a single value - * or of one of multiple values. - */ -class ReturnKind extends TReturnKind { - int i; - - ReturnKind() { this = MkReturnKind(i) } - - /** Gets the index of this return value. */ - int getIndex() { result = i } - - /** Gets a textual representation of this return kind. */ - string toString() { result = "return[" + i + "]" } -} - -/** - * Gets a node that can read the value returned from `call` with return kind - * `kind`. - */ -OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { - exists(DataFlow::CallNode c, int i | c.asExpr() = call and kind = MkReturnKind(i) | - result = c.getResult(i) - ) -} - -/** - * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local - * (intra-procedural) step, not taking function models into account. - */ -predicate basicLocalFlowStep(Node nodeFrom, Node nodeTo) { - // Instruction -> Instruction - exists(Expr pred, Expr succ | - succ.(LogicalBinaryExpr).getAnOperand() = pred or - succ.(ConversionExpr).getOperand() = pred - | - nodeFrom = exprNode(pred) and - nodeTo = exprNode(succ) - ) - or - // Type assertion: if in the context `checked, ok := e.(*Type)` (in which - // case tuple-extraction instructions exist), flow from `e` to `e.(*Type)[0]`; - // otherwise flow from `e` to `e.(*Type)`. - exists(IR::Instruction evalAssert, TypeAssertExpr assert | - nodeFrom.asExpr() = assert.getExpr() and - evalAssert = IR::evalExprInstruction(assert) and - if exists(IR::extractTupleElement(evalAssert, _)) - then nodeTo.asInstruction() = IR::extractTupleElement(evalAssert, 0) - else nodeTo.asInstruction() = evalAssert - ) - or - // Instruction -> SSA - exists(IR::Instruction pred, SsaExplicitDefinition succ | - succ.getRhs() = pred and - nodeFrom = instructionNode(pred) and - nodeTo = ssaNode(succ) - ) - or - // SSA -> SSA - exists(SsaDefinition pred, SsaDefinition succ | - succ.(SsaVariableCapture).getSourceVariable() = pred.(SsaExplicitDefinition).getSourceVariable() or - succ.(SsaPseudoDefinition).getAnInput() = pred - | - nodeFrom = ssaNode(pred) and - nodeTo = ssaNode(succ) - ) - or - // SSA -> Instruction - exists(SsaDefinition pred, IR::Instruction succ | - succ = pred.getVariable().getAUse() and - nodeFrom = ssaNode(pred) and - nodeTo = instructionNode(succ) - ) - or - // GlobalFunctionNode -> use - nodeFrom = - any(GlobalFunctionNode fn | fn.getFunction() = nodeTo.asExpr().(FunctionName).getTarget()) -} - -/** - * Holds if data can flow from `node1` to `node2` in a way that loses the - * calling context. For example, this would happen with flow through a - * global or static variable. - */ -predicate jumpStep(Node n1, Node n2) { - exists(ValueEntity v, Write w | - not v instanceof SsaSourceVariable and - not v instanceof Field and - w.writes(v, n1) and - n2 = v.getARead() - ) -} - -/** - * Holds if data can flow from `node1` to `node2` via an assignment to `c`. - * Thus, `node2` references an object with a content `x` that contains the - * value of `node1`. - */ -predicate storeStep(Node node1, Content c, Node node2) { - // a write `(*p).f = rhs` is modelled as two store steps: `rhs` is flows into field `f` of `(*p)`, - // which in turn flows into the pointer content of `p` - exists(Write w, Field f, DataFlow::Node base, DataFlow::Node rhs | w.writesField(base, f, rhs) | - node1 = rhs and - node2.(PostUpdateNode).getPreUpdateNode() = base and - c = any(DataFlow::FieldContent fc | fc.getField() = f) - or - node1 = base and - node2.(PostUpdateNode).getPreUpdateNode() = node1.(PointerDereferenceNode).getOperand() and - c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType()) - ) - or - node1 = node2.(AddressOperationNode).getOperand() and - c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType()) - or - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) - or - containerStoreStep(node1, node2, c) -} - -/** - * Holds if data can flow from `node1` to `node2` via a read of `c`. - * Thus, `node1` references an object with a content `c` whose value ends up in - * `node2`. - */ -predicate readStep(Node node1, Content c, Node node2) { - node1 = node2.(PointerDereferenceNode).getOperand() and - c = any(DataFlow::PointerContent pc | pc.getPointerType() = node1.getType()) - or - exists(FieldReadNode read | - node2 = read and - node1 = read.getBase() and - c = any(DataFlow::FieldContent fc | fc.getField() = read.getField()) - ) - or - FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) - or - containerReadStep(node1, node2, c) -} - -/** - * Holds if values stored inside content `c` are cleared at node `n`. - */ -predicate clearsContent(Node n, Content c) { - // Because our post-update nodes are shared between multiple pre-update - // nodes, attempting to clear content causes summary stores into arg in - // particular to malfunction. - none() - // c instanceof FieldContent and - // FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) - // or - // FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) -} - -/** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(Node n) { - result = n.getType() - or - result = FlowSummaryImpl::Private::summaryNodeType(n) -} - -/** Gets a string representation of a type returned by `getNodeType()`. */ -string ppReprType(Type t) { result = t.toString() } - -/** - * Holds if `t1` and `t2` are compatible, that is, whether data can flow from - * a node of type `t1` to a node of type `t2`. - */ -pragma[inline] -predicate compatibleTypes(Type t1, Type t2) { - any() // stub implementation -} - -////////////////////////////////////////////////////////////////////////////// -// Java QL library compatibility wrappers -////////////////////////////////////////////////////////////////////////////// -/** A node that performs a type cast. */ -class CastNode extends ExprNode { - override ConversionExpr expr; -} - -class DataFlowExpr = Expr; - -class DataFlowType = Type; - -class DataFlowLocation = Location; - -private newtype TDataFlowCallable = - TCallable(Callable c) or - TFileScope(File f) - -class DataFlowCallable extends TDataFlowCallable { - Callable asCallable() { this = TCallable(result) } - - File asFileScope() { this = TFileScope(result) } - - FuncDef getFuncDef() { result = this.asCallable().getFuncDef() } - - Function asFunction() { result = this.asCallable().asFunction() } - - FuncLit asFuncLit() { result = this.asCallable().asFuncLit() } - - SignatureType getType() { result = this.asCallable().getType() } - - string toString() { - result = this.asCallable().toString() or - result = "File scope: " + this.asFileScope().toString() - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asCallable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asFileScope().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** A function call relevant for data flow. */ -class DataFlowCall extends Expr { - DataFlow::CallNode call; - - DataFlowCall() { this = call.asExpr() } - - /** - * Gets the nth argument for this call. - */ - Node getArgument(int n) { result = call.getArgument(n) } - - /** Gets the data flow node corresponding to this call. */ - ExprNode getNode() { result = call } - - /** Gets the enclosing callable of this call. */ - DataFlowCallable getEnclosingCallable() { - result.asCallable().getFuncDef() = this.getEnclosingFunction() - or - not exists(this.getEnclosingFunction()) and result.asFileScope() = this.getFile() - } -} - -/** Holds if `e` is an expression that always has the same Boolean value `val`. */ -private predicate constantBooleanExpr(Expr e, boolean val) { - e.getBoolValue() = val - or - exists(SsaExplicitDefinition v, Expr src | - IR::evalExprInstruction(e) = v.getVariable().getAUse() and - IR::evalExprInstruction(src) = v.getRhs() and - constantBooleanExpr(src, val) - ) -} - -/** An argument that always has the same Boolean value. */ -private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode { - ConstantBooleanArgumentNode() { constantBooleanExpr(this.getExpr(), _) } - - /** Gets the Boolean value of this expression. */ - boolean getBooleanValue() { constantBooleanExpr(this.getExpr(), result) } -} - -/** - * Returns a guard that will certainly not hold in calling context `call`. - * - * In particular it does not hold because it checks that `param` has value `b`, but - * in context `call` it is known to have value `!b`. Note this is `noinline`d in order - * to avoid a bad join order in `isUnreachableInCall`. - */ -pragma[noinline] -private ControlFlow::ConditionGuardNode getAFalsifiedGuard(DataFlowCall call) { - exists(SsaParameterNode param, ConstantBooleanArgumentNode arg | - // get constant bool argument and parameter for this call - viableParamArg(call, pragma[only_bind_into](param), pragma[only_bind_into](arg)) and - // which is used in a guard controlling `n` with the opposite value of `arg` - result.ensures(param.getAUse(), arg.getBooleanValue().booleanNot()) - ) -} - -/** - * Holds if the node `n` is unreachable when the call context is `call`. - */ -predicate isUnreachableInCall(Node n, DataFlowCall call) { - getAFalsifiedGuard(call).dominates(n.getBasicBlock()) -} - -int accessPathLimit() { result = 5 } - -/** - * Holds if access paths with `c` at their head always should be tracked at high - * precision. This disables adaptive access path precision for such access paths. - */ -predicate forceHighPrecision(Content c) { - c instanceof ArrayContent or c instanceof CollectionContent -} - -/** The unit type. */ -private newtype TUnit = TMkUnit() - -/** The trivial type with a single element. */ -class Unit extends TUnit { - /** Gets a textual representation of this element. */ - string toString() { result = "unit" } -} - -/** - * Gets the `i`th argument of call `c`, where the receiver of a method call - * counts as argument -1. - */ -Node getArgument(CallNode c, int i) { - result = c.getArgument(i) - or - result = c.(MethodCallNode).getReceiver() and - i = -1 -} - -/** Holds if `n` should be hidden from path explanations. */ -predicate nodeIsHidden(Node n) { none() } - -class LambdaCallKind = Unit; - -/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ -predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() } - -/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ -predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() } - -/** Extra data-flow steps needed for lambda flow analysis. */ -predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } - -/** - * 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. - * - * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed - * by default as a heuristic. - */ -predicate allowParameterReturnInSelf(ParameterNode p) { - FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p) -} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll deleted file mode 100644 index 47fa923aa..000000000 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll +++ /dev/null @@ -1,456 +0,0 @@ -/** - * Provides Go-specific definitions for use in the data flow library. - */ - -import go -import semmle.go.dataflow.FunctionInputsAndOutputs -private import semmle.go.dataflow.ExternalFlow -private import DataFlowPrivate -private import FlowSummaryImpl as FlowSummaryImpl -import DataFlowNodes::Public - -/** - * Holds if `node` reads an element from `base`, either via an element-read (`base[y]`) expression - * or via a range statement `_, node := range base`. - */ -predicate readsAnElement(DataFlow::Node node, DataFlow::Node base) { - node.(ElementReadNode).readsElement(base, _) or - node.(RangeElementNode).getBase() = base -} - -/** - * A model of a function specifying that the function copies input values from - * a parameter or qualifier to a result. - * - * Note that this only models verbatim copying. Flow that does not preserve exact - * values should be modeled by `TaintTracking::FunctionModel` instead. - */ -abstract class FunctionModel extends Function { - /** Holds if data flows through this function from `input` to `output`. */ - abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output); - - /** Gets an input node for this model for the call `c`. */ - DataFlow::Node getAnInputNode(DataFlow::CallNode c) { this.flowStepForCall(result, _, c) } - - /** Gets an output node for this model for the call `c`. */ - DataFlow::Node getAnOutputNode(DataFlow::CallNode c) { this.flowStepForCall(_, result, c) } - - /** Holds if this function model causes data to flow from `pred` to `succ` for the call `c`. */ - predicate flowStepForCall(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode c) { - c = this.getACall() and - exists(FunctionInput inp, FunctionOutput outp | this.hasDataFlow(inp, outp) | - pred = inp.getNode(c) and - succ = outp.getNode(c) - ) - } - - /** Holds if this function model causes data to flow from `pred` to `succ`. */ - predicate flowStep(DataFlow::Node pred, DataFlow::Node succ) { - this.flowStepForCall(pred, succ, _) - } -} - -/** - * Gets the `Node` corresponding to `insn`. - */ -InstructionNode instructionNode(IR::Instruction insn) { result.asInstruction() = insn } - -/** - * Gets the `Node` corresponding to `e`. - */ -ExprNode exprNode(Expr e) { result.asExpr() = e.stripParens() } - -/** - * Gets the `Node` corresponding to the value of `p` at function entry. - */ -ParameterNode parameterNode(Parameter p) { result.asParameter() = p } - -/** - * Gets the `Node` corresponding to the value of `r` at function entry. - */ -ReceiverNode receiverNode(ReceiverVariable r) { result.asReceiverVariable() = r } - -/** - * Gets the data-flow node corresponding to SSA variable `v`. - */ -SsaNode ssaNode(SsaVariable v) { result.getDefinition() = v.getDefinition() } - -/** - * Gets the data-flow node corresponding to the `i`th element of tuple `t` (which is either a call - * with multiple results, an iterator in a range loop, or the result of a type assertion). - */ -Node extractTupleElement(Node t, int i) { - exists(IR::Instruction insn | t = instructionNode(insn) | - result = instructionNode(IR::extractTupleElement(insn, i)) - ) -} - -/** - * Holds if `node` refers to a value returned alongside a non-nil error value. - * - * For example, `0` in `func tryGetInt() (int, error) { return 0, errors.New("no good") }` - */ -predicate isReturnedWithError(Node node) { - exists(ReturnStmt ret, int nodeArg, int errorArg | - ret.getExpr(nodeArg) = node.asExpr() and - nodeArg != errorArg and - ret.getExpr(errorArg).getType() instanceof ErrorType - // That last condition implies ret.getExpr(errorArg) is non-nil, since nil doesn't implement `error` - ) -} - -/** - * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local - * (intra-procedural) step. - */ -predicate localFlowStep(Node nodeFrom, Node nodeTo) { - simpleLocalFlowStep(nodeFrom, nodeTo) - or - // Simple flow through library code is included in the exposed local - // step relation, even though flow is technically inter-procedural - FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true) -} - -/** - * INTERNAL: do not use. - * - * This is the local flow predicate that's used as a building block in global - * data flow. It may have less flow than the `localFlowStep` predicate. - */ -cached -predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - basicLocalFlowStep(nodeFrom, nodeTo) - or - // step through function model - any(FunctionModel m).flowStep(nodeFrom, nodeTo) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) -} - -/** - * Holds if data flows from `source` to `sink` in zero or more local - * (intra-procedural) steps. - */ -predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } - -private newtype TContent = - TFieldContent(Field f) or - TCollectionContent() or - TArrayContent() or - TPointerContent(PointerType p) or - TMapKeyContent() or - TMapValueContent() or - TSyntheticFieldContent(SyntheticField s) - -/** - * A reference contained in an object. Examples include instance fields, the - * contents of a collection object, the contents of an array or pointer. - */ -class Content extends TContent { - /** Gets the type of the contained data for the purpose of type pruning. */ - DataFlowType getType() { result instanceof EmptyInterfaceType } - - /** Gets a textual representation of this element. */ - abstract string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 - } -} - -/** A reference through a field. */ -class FieldContent extends Content, TFieldContent { - Field f; - - FieldContent() { this = TFieldContent(f) } - - /** Gets the field associated with this `FieldContent`. */ - Field getField() { result = f } - - override DataFlowType getType() { result = f.getType() } - - override string toString() { result = f.toString() } - - override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - f.getDeclaration().hasLocationInfo(path, sl, sc, el, ec) - } -} - -/** A reference through the contents of some collection-like container. */ -class CollectionContent extends Content, TCollectionContent { - override string toString() { result = "collection" } -} - -/** A reference through an array. */ -class ArrayContent extends Content, TArrayContent { - override string toString() { result = "array" } -} - -/** A reference through a pointer. */ -class PointerContent extends Content, TPointerContent { - PointerType t; - - PointerContent() { this = TPointerContent(t) } - - /** Gets the pointer type that containers with this content must have. */ - PointerType getPointerType() { result = t } - - override DataFlowType getType() { result = t.getBaseType() } - - override string toString() { result = "pointer" } -} - -/** A reference through a map key. */ -class MapKeyContent extends Content, TMapKeyContent { - override string toString() { result = "map.key" } -} - -/** A reference through a map value. */ -class MapValueContent extends Content, TMapValueContent { - override string toString() { result = "map.value" } -} - -class SyntheticFieldContent extends Content, TSyntheticFieldContent { - SyntheticField s; - - SyntheticFieldContent() { this = TSyntheticFieldContent(s) } - - /** Gets the field associated with this `SyntheticFieldContent`. */ - SyntheticField getField() { result = s } - - override DataFlowType getType() { result = s.getType() } - - override string toString() { result = s.toString() } -} - -/** - * A guard that validates some expression. - * - * To use this in a configuration, extend the class and provide a - * characteristic predicate precisely specifying the guard, and override - * `checks` to specify what is being validated and in which branch. - * - * When using a data-flow or taint-flow configuration `cfg`, it is important - * that any classes extending BarrierGuard in scope which are not used in `cfg` - * are disjoint from any classes extending BarrierGuard in scope which are used - * in `cfg`. - */ -abstract class BarrierGuard extends Node { - /** Holds if this guard validates `e` upon evaluating to `branch`. */ - abstract predicate checks(Expr e, boolean branch); - - /** Gets a node guarded by this guard. */ - final Node getAGuardedNode() { - exists(ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields var | - result = var.getAUse() - | - this.guards(guard, nd, var) and - guard.dominates(result.getBasicBlock()) - ) - } - - /** - * Holds if `guard` markes a point in the control-flow graph where this node - * is known to validate `nd`, which is represented by `ap`. - * - * This predicate exists to enforce a good join order in `getAGuardedNode`. - */ - pragma[noinline] - private predicate guards(ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap) { - this.guards(guard, nd) and nd = ap.getAUse() - } - - /** - * Holds if `guard` markes a point in the control-flow graph where this node - * is known to validate `nd`. - */ - private predicate guards(ControlFlow::ConditionGuardNode guard, Node nd) { - exists(boolean branch | - this.checks(nd.asExpr(), branch) and - guard.ensures(this, branch) - ) - or - exists( - Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c, - Node resNode, Node check, boolean outcome - | - this.guardingCall(f, inp, outp, p, c, nd, resNode) and - p.checkOn(check, outcome, resNode) and - guard.ensures(pragma[only_bind_into](check), outcome) - ) - } - - pragma[noinline] - private predicate guardingCall( - Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c, Node nd, - Node resNode - ) { - this.guardingFunction(f, inp, outp, p) and - c = f.getACall() and - nd = inp.getNode(c) and - localFlow(pragma[only_bind_out](outp.getNode(c)), resNode) - } - - /** - * Holds if whenever `p` holds of output `outp` of function `f`, this node - * is known to validate the input `inp` of `f`. - * - * We check this by looking for guards on `inp` that dominate a `return` statement that - * is the only `return` in `f` that can return `true`. This means that if `f` returns `true`, - * the guard must have been satisfied. (Similar reasoning is applied for statements returning - * `false`, `nil` or a non-`nil` value.) - */ - private predicate guardingFunction( - Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p - ) { - exists(FuncDecl fd, Node arg, Node ret | - fd.getFunction() = f and - localFlow(inp.getExitNode(fd), arg) and - ret = outp.getEntryNode(fd) and - ( - // Case: a function like "if someBarrierGuard(arg) { return true } else { return false }" - exists(ControlFlow::ConditionGuardNode guard | - this.guards(guard, arg) and - guard.dominates(ret.getBasicBlock()) - | - exists(boolean b | - onlyPossibleReturnOfBool(fd, outp, ret, b) and - p.isBoolean(b) - ) - or - onlyPossibleReturnOfNonNil(fd, outp, ret) and - p.isNonNil() - or - onlyPossibleReturnOfNil(fd, outp, ret) and - p.isNil() - ) - or - // Case: a function like "return someBarrierGuard(arg)" - // or "return !someBarrierGuard(arg) && otherCond(...)" - exists(boolean outcome | - ret = getUniqueOutputNode(fd, outp) and - this.checks(arg.asExpr(), outcome) and - // This predicate's contract is (p holds of ret ==> arg is checked), - // (and we have (this has outcome ==> arg is checked)) - // but p.checkOn(ret, outcome, this) gives us (ret has outcome ==> p holds of this), - // so we need to swap outcome and (specifically boolean) p: - DataFlow::booleanProperty(outcome).checkOn(ret, p.asBoolean(), this) - ) - or - // Case: a function like "return guardProxy(arg)" - // or "return !guardProxy(arg) || otherCond(...)" - exists( - Function f2, FunctionInput inp2, FunctionOutput outp2, CallNode c, - DataFlow::Property outpProp - | - ret = getUniqueOutputNode(fd, outp) and - this.guardingFunction(f2, inp2, outp2, outpProp) and - c = f2.getACall() and - arg = inp2.getNode(c) and - ( - // See comment above ("This method's contract...") for rationale re: the inversion of - // `p` and `outpProp` here: - outpProp.checkOn(ret, p.asBoolean(), outp2.getNode(c)) - or - // The particular case where p is non-boolean (i.e., nil or non-nil), and we directly return `c`: - outpProp = p and ret = outp2.getNode(c) - ) - ) - ) - ) - } -} - -DataFlow::Node getUniqueOutputNode(FuncDecl fd, FunctionOutput outp) { - result = unique(DataFlow::Node n | n = outp.getEntryNode(fd) | n) -} - -/** - * Holds if `ret` is a data-flow node whose value contributes to the output `res` of `fd`, - * and that node may have Boolean value `b`. - */ -predicate possiblyReturnsBool(FuncDecl fd, FunctionOutput res, Node ret, Boolean b) { - ret = res.getEntryNode(fd) and - ret.getType().getUnderlyingType() instanceof BoolType and - not ret.getBoolValue() != b -} - -/** - * Holds if `ret` is the only data-flow node whose value contributes to the output `res` of `fd` - * that may have Boolean value `b`, since all the other output nodes have a Boolean value - * other than `b`. - */ -private predicate onlyPossibleReturnOfBool(FuncDecl fd, FunctionOutput res, Node ret, boolean b) { - possiblyReturnsBool(fd, res, ret, b) and - forall(Node otherRet | otherRet = res.getEntryNode(fd) and otherRet != ret | - otherRet.getBoolValue() != b - ) -} - -/** - * Holds if `ret` is a data-flow node whose value contributes to the output `res` of `fd`, - * and that node may evaluate to a value other than `nil`. - */ -predicate possiblyReturnsNonNil(FuncDecl fd, FunctionOutput res, Node ret) { - ret = res.getEntryNode(fd) and - not ret.asExpr() = Builtin::nil().getAReference() -} - -/** - * Holds if `ret` is the only data-flow node whose value contributes to the output `res` of `fd` - * that may have a value other than `nil`, since all the other output nodes evaluate to `nil`. - */ -private predicate onlyPossibleReturnOfNonNil(FuncDecl fd, FunctionOutput res, Node ret) { - possiblyReturnsNonNil(fd, res, ret) and - forall(Node otherRet | otherRet = res.getEntryNode(fd) and otherRet != ret | - otherRet.asExpr() = Builtin::nil().getAReference() - ) -} - -/** - * Holds if function `f`'s result `output`, which must be a return value, cannot be nil. - */ -private predicate certainlyReturnsNonNil(Function f, FunctionOutput output) { - output.isResult(_) and - ( - f.hasQualifiedName("errors", "New") - or - f.hasQualifiedName("fmt", "Errorf") - or - f in [Builtin::new(), Builtin::make()] - or - exists(FuncDecl fd | fd = f.getFuncDecl() | - forex(DataFlow::Node ret | ret = output.getEntryNode(fd) | isCertainlyNotNil(ret)) - ) - ) -} - -/** - * Holds if `node` cannot be `nil`. - */ -private predicate isCertainlyNotNil(DataFlow::Node node) { - node instanceof DataFlow::AddressOperationNode - or - exists(DataFlow::CallNode c, FunctionOutput output | output.getExitNode(c) = node | - certainlyReturnsNonNil(c.getTarget(), output) - ) -} - -/** - * Holds if `ret` is the only data-flow node whose value contributes to the output `res` of `fd` - * that returns `nil`, since all the other output nodes are known to be non-nil. - */ -private predicate onlyPossibleReturnOfNil(FuncDecl fd, FunctionOutput res, DataFlow::Node ret) { - ret = res.getEntryNode(fd) and - ret.asExpr() = Builtin::nil().getAReference() and - forall(DataFlow::Node otherRet | otherRet = res.getEntryNode(fd) and otherRet != ret | - isCertainlyNotNil(otherRet) - ) -} diff --git a/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll deleted file mode 100644 index e59c96a5c..000000000 --- a/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll +++ /dev/null @@ -1,1155 +0,0 @@ -/** - * Provides classes and predicates for defining flow summaries. - * - * The definitions in this file are language-independent, and language-specific - * definitions are passed in via the `DataFlowImplSpecific` and - * `FlowSummaryImplSpecific` modules. - */ - -private import FlowSummaryImplSpecific -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommon - -/** Provides classes and predicates for defining flow summaries. */ -module Public { - private import Private - - /** - * A component used in a flow summary. - * - * Either a parameter or an argument at a given position, a specific - * content type, or a return kind. - */ - class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(Content c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos - ) - or - exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos - ) - or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - } - } - - /** Provides predicates for constructing summary components. */ - module SummaryComponent { - /** Gets a summary component for content `c`. */ - SummaryComponent content(Content c) { result = TContentSummaryComponent(c) } - - /** Gets a summary component for a parameter at position `pos`. */ - SummaryComponent parameter(ArgumentPosition pos) { result = TParameterSummaryComponent(pos) } - - /** Gets a summary component for an argument at position `pos`. */ - SummaryComponent argument(ParameterPosition pos) { result = TArgumentSummaryComponent(pos) } - - /** Gets a summary component for a return of kind `rk`. */ - SummaryComponent return(ReturnKind rk) { result = TReturnSummaryComponent(rk) } - } - - /** - * A (non-empty) stack of summary components. - * - * A stack is used to represent where data is read from (input) or where it - * is written to (output). For example, an input stack `[Field f, Argument 0]` - * means that data is read from field `f` from the `0`th argument, while an - * output stack `[Field g, Return]` means that data is written to the field - * `g` of the returned object. - */ - class SummaryComponentStack extends TSummaryComponentStack { - /** Gets the head of this stack. */ - SummaryComponent head() { - this = TSingletonSummaryComponentStack(result) or - this = TConsSummaryComponentStack(result, _) - } - - /** Gets the tail of this stack, if any. */ - SummaryComponentStack tail() { this = TConsSummaryComponentStack(_, result) } - - /** Gets the length of this stack. */ - int length() { - this = TSingletonSummaryComponentStack(_) and result = 1 - or - result = 1 + this.tail().length() - } - - /** Gets the stack obtained by dropping the first `i` elements, if any. */ - SummaryComponentStack drop(int i) { - i = 0 and result = this - or - result = this.tail().drop(i - 1) - } - - /** Holds if this stack contains summary component `c`. */ - predicate contains(SummaryComponent c) { c = this.drop(_).head() } - - /** Gets the bottom element of this stack. */ - SummaryComponent bottom() { - this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() - } - - /** Gets a textual representation of this stack. */ - string toString() { - exists(SummaryComponent head, SummaryComponentStack tail | - head = this.head() and - tail = this.tail() and - result = tail + "." + head - ) - or - exists(SummaryComponent c | - this = TSingletonSummaryComponentStack(c) and - result = c.toString() - ) - } - } - - /** Provides predicates for constructing stacks of summary components. */ - module SummaryComponentStack { - /** Gets a singleton stack containing `c`. */ - SummaryComponentStack singleton(SummaryComponent c) { - result = TSingletonSummaryComponentStack(c) - } - - /** - * Gets the stack obtained by pushing `head` onto `tail`. - * - * Make sure to override `RequiredSummaryComponentStack::required()` in order - * to ensure that the constructed stack exists. - */ - SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) { - result = TConsSummaryComponentStack(head, tail) - } - - /** Gets a singleton stack for an argument at position `pos`. */ - SummaryComponentStack argument(ParameterPosition pos) { - result = singleton(SummaryComponent::argument(pos)) - } - - /** Gets a singleton stack representing a return of kind `rk`. */ - SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } - } - - private predicate noComponentSpecificCsv(SummaryComponent sc) { - not exists(getComponentSpecificCsv(sc)) - } - - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponentCsv(SummaryComponent sc) { - result = getComponentSpecificCsv(sc) - or - noComponentSpecificCsv(sc) and - ( - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPositionCsv(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPositionCsv(pos) + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - ) - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStackCsv(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStackCsv(tail) + "." + getComponentCsv(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponentCsv(c) - ) - } - - /** - * A class that exists for QL technical reasons only (the IPA type used - * to represent component stacks needs to be bounded). - */ - class RequiredSummaryComponentStack extends Unit { - /** - * Holds if the stack obtained by pushing `head` onto `tail` is required. - */ - abstract predicate required(SummaryComponent head, SummaryComponentStack tail); - } - - /** A callable with a flow summary. */ - abstract class SummarizedCallable extends DataFlowCallable { - /** - * Holds if data may flow from `input` to `output` through this callable. - * - * `preservesValue` indicates whether this is a value-preserving step - * or a taint-step. - * - * Input specifications are restricted to stacks that end with - * `SummaryComponent::argument(_)`, preceded by zero or more - * `SummaryComponent::return(_)` or `SummaryComponent::content(_)` components. - * - * Output specifications are restricted to stacks that end with - * `SummaryComponent::return(_)` or `SummaryComponent::argument(_)`. - * - * Output stacks ending with `SummaryComponent::return(_)` can be preceded by zero - * or more `SummaryComponent::content(_)` components. - * - * Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an - * optional `SummaryComponent::parameter(_)` component, which in turn can be preceded - * by zero or more `SummaryComponent::content(_)` components. - */ - pragma[nomagic] - predicate propagatesFlow( - SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - ) { - none() - } - - /** - * Holds if values stored inside `content` are cleared on objects passed as - * arguments at position `pos` to this callable. - */ - pragma[nomagic] - predicate clearsContent(ParameterPosition pos, Content content) { none() } - } -} - -/** - * Provides predicates for compiling flow summaries down to atomic local steps, - * read steps, and store steps. - */ -module Private { - private import Public - import AccessPathSyntax - - newtype TSummaryComponent = - TContentSummaryComponent(Content c) or - TParameterSummaryComponent(ArgumentPosition pos) or - TArgumentSummaryComponent(ParameterPosition pos) or - TReturnSummaryComponent(ReturnKind rk) - - private TParameterSummaryComponent thisParam() { - result = TParameterSummaryComponent(instanceParameterPosition()) - } - - newtype TSummaryComponentStack = - TSingletonSummaryComponentStack(SummaryComponent c) or - TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) { - any(RequiredSummaryComponentStack x).required(head, tail) - or - any(RequiredSummaryComponentStack x).required(TParameterSummaryComponent(_), tail) and - head = thisParam() - or - derivedFluentFlowPush(_, _, _, head, tail, _) - } - - pragma[nomagic] - private predicate summary( - SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output, - boolean preservesValue - ) { - c.propagatesFlow(input, output, preservesValue) - or - // observe side effects of callbacks on input arguments - c.propagatesFlow(output, input, preservesValue) and - preservesValue = true and - isCallbackParameter(input) and - isContentOfArgument(output, _) - or - // flow from the receiver of a callback into the instance-parameter - exists(SummaryComponentStack s, SummaryComponentStack callbackRef | - c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _) - | - callbackRef = s.drop(_) and - (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and - input = callbackRef.tail() and - output = TConsSummaryComponentStack(thisParam(), input) and - preservesValue = true - ) - or - exists(SummaryComponentStack arg, SummaryComponentStack return | - derivedFluentFlow(c, input, arg, return, preservesValue) - | - arg.length() = 1 and - output = return - or - exists(SummaryComponent head, SummaryComponentStack tail | - derivedFluentFlowPush(c, input, arg, head, tail, 0) and - output = SummaryComponentStack::push(head, tail) - ) - ) - or - // Chain together summaries where values get passed into callbacks along the way - exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 | - c.propagatesFlow(input, mid, preservesValue1) and - c.propagatesFlow(mid, output, preservesValue2) and - mid.drop(mid.length() - 2) = - SummaryComponentStack::push(TParameterSummaryComponent(_), - SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and - preservesValue = preservesValue1.booleanAnd(preservesValue2) - ) - } - - /** - * Holds if `c` has a flow summary from `input` to `arg`, where `arg` - * writes to (contents of) arguments at position `pos`, and `c` has a - * value-preserving flow summary from the arguments at position `pos` - * to a return value (`return`). - * - * In such a case, we derive flow from `input` to (contents of) the return - * value. - * - * As an example, this simplifies modeling of fluent methods: - * for `StringBuilder.append(x)` with a specified value flow from qualifier to - * return value and taint flow from argument 0 to the qualifier, then this - * allows us to infer taint flow from argument 0 to the return value. - */ - pragma[nomagic] - private predicate derivedFluentFlow( - SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg, - SummaryComponentStack return, boolean preservesValue - ) { - exists(ParameterPosition pos | - summary(c, input, arg, preservesValue) and - isContentOfArgument(arg, pos) and - summary(c, SummaryComponentStack::argument(pos), return, true) and - return.bottom() = TReturnSummaryComponent(_) - ) - } - - pragma[nomagic] - private predicate derivedFluentFlowPush( - SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg, - SummaryComponent head, SummaryComponentStack tail, int i - ) { - derivedFluentFlow(c, input, arg, tail, _) and - head = arg.drop(i).head() and - i = arg.length() - 2 - or - exists(SummaryComponent head0, SummaryComponentStack tail0 | - derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and - head = arg.drop(i).head() and - tail = SummaryComponentStack::push(head0, tail0) - ) - } - - private predicate isCallbackParameter(SummaryComponentStack s) { - s.head() = TParameterSummaryComponent(_) and exists(s.tail()) - } - - private predicate isContentOfArgument(SummaryComponentStack s, ParameterPosition pos) { - s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), pos) - or - s = SummaryComponentStack::argument(pos) - } - - private predicate outputState(SummarizedCallable c, SummaryComponentStack s) { - summary(c, _, s, _) - or - exists(SummaryComponentStack out | - outputState(c, out) and - out.head() = TContentSummaryComponent(_) and - s = out.tail() - ) - or - // Add the argument node corresponding to the requested post-update node - inputState(c, s) and isCallbackParameter(s) - } - - private predicate inputState(SummarizedCallable c, SummaryComponentStack s) { - summary(c, s, _, _) - or - exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail()) - or - exists(SummaryComponentStack out | - outputState(c, out) and - out.head() = TParameterSummaryComponent(_) and - s = out.tail() - ) - } - - private newtype TSummaryNodeState = - TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or - TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or - TSummaryNodeClearsContentState(ParameterPosition pos, boolean post) { - any(SummarizedCallable sc).clearsContent(pos, _) and post in [false, true] - } - - /** - * A state used to break up (complex) flow summaries into atomic flow steps. - * For a flow summary - * - * ```ql - * propagatesFlow( - * SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - * ) - * ``` - * - * the following states are used: - * - * - `TSummaryNodeInputState(SummaryComponentStack s)`: - * this state represents that the components in `s` _have been read_ from the - * input. - * - `TSummaryNodeOutputState(SummaryComponentStack s)`: - * this state represents that the components in `s` _remain to be written_ to - * the output. - */ - class SummaryNodeState extends TSummaryNodeState { - /** Holds if this state is a valid input state for `c`. */ - pragma[nomagic] - predicate isInputState(SummarizedCallable c, SummaryComponentStack s) { - this = TSummaryNodeInputState(s) and - inputState(c, s) - } - - /** Holds if this state is a valid output state for `c`. */ - pragma[nomagic] - predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) { - this = TSummaryNodeOutputState(s) and - outputState(c, s) - } - - /** Gets a textual representation of this state. */ - string toString() { - exists(SummaryComponentStack s | - this = TSummaryNodeInputState(s) and - result = "read: " + s - ) - or - exists(SummaryComponentStack s | - this = TSummaryNodeOutputState(s) and - result = "to write: " + s - ) - or - exists(ParameterPosition pos, boolean post, string postStr | - this = TSummaryNodeClearsContentState(pos, post) and - (if post = true then postStr = " (post)" else postStr = "") and - result = "clear: " + pos + postStr - ) - } - } - - /** - * Holds if `state` represents having read from a parameter at position - * `pos` in `c`. In this case we are not synthesizing a data-flow node, - * but instead assume that a relevant parameter node already exists. - */ - private predicate parameterReadState( - SummarizedCallable c, SummaryNodeState state, ParameterPosition pos - ) { - state.isInputState(c, SummaryComponentStack::argument(pos)) - } - - /** - * Holds if a synthesized summary node is needed for the state `state` in summarized - * callable `c`. - */ - predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) { - state.isInputState(c, _) and - not parameterReadState(c, state, _) - or - state.isOutputState(c, _) - or - exists(ParameterPosition pos | - c.clearsContent(pos, _) and - state = TSummaryNodeClearsContentState(pos, _) - ) - } - - pragma[noinline] - private Node summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) { - exists(SummaryNodeState state | state.isInputState(c, s) | - result = summaryNode(c, state) - or - exists(ParameterPosition pos | - parameterReadState(c, state, pos) and - result.(ParamNode).isParameterOf(c, pos) - ) - ) - } - - pragma[noinline] - private Node summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) { - exists(SummaryNodeState state | - state.isOutputState(c, s) and - result = summaryNode(c, state) - ) - } - - /** - * Holds if a write targets `post`, which is a post-update node for a - * parameter at position `pos` in `c`. - */ - private predicate isParameterPostUpdate(Node post, SummarizedCallable c, ParameterPosition pos) { - post = summaryNodeOutputState(c, SummaryComponentStack::argument(pos)) - } - - /** Holds if a parameter node at position `pos` is required for `c`. */ - predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) { - parameterReadState(c, _, pos) - or - isParameterPostUpdate(_, c, pos) - or - c.clearsContent(pos, _) - } - - private predicate callbackOutput( - SummarizedCallable c, SummaryComponentStack s, Node receiver, ReturnKind rk - ) { - any(SummaryNodeState state).isInputState(c, s) and - s.head() = TReturnSummaryComponent(rk) and - receiver = summaryNodeInputState(c, s.drop(1)) - } - - private predicate callbackInput( - SummarizedCallable c, SummaryComponentStack s, Node receiver, ArgumentPosition pos - ) { - any(SummaryNodeState state).isOutputState(c, s) and - s.head() = TParameterSummaryComponent(pos) and - receiver = summaryNodeInputState(c, s.drop(1)) - } - - /** Holds if a call targeting `receiver` should be synthesized inside `c`. */ - predicate summaryCallbackRange(SummarizedCallable c, Node receiver) { - callbackOutput(c, _, receiver, _) - or - callbackInput(c, _, receiver, _) - } - - /** - * Gets the type of synthesized summary node `n`. - * - * The type is computed based on the language-specific predicates - * `getContentType()`, `getReturnType()`, `getCallbackParameterType()`, and - * `getCallbackReturnType()`. - */ - DataFlowType summaryNodeType(Node n) { - exists(Node pre | - summaryPostUpdateNode(n, pre) and - result = getNodeType(pre) - ) - or - exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() | - n = summaryNodeInputState(c, s) and - ( - exists(Content cont | - head = TContentSummaryComponent(cont) and result = getContentType(cont) - ) - or - exists(ReturnKind rk | - head = TReturnSummaryComponent(rk) and - result = - getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), - s.drop(1))), rk) - ) - ) - or - n = summaryNodeOutputState(c, s) and - ( - exists(Content cont | - head = TContentSummaryComponent(cont) and result = getContentType(cont) - ) - or - s.length() = 1 and - exists(ReturnKind rk | - head = TReturnSummaryComponent(rk) and - result = getReturnType(c, rk) - ) - or - exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) | - result = - getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), - s.drop(1))), pos) - ) - ) - ) - or - exists(SummarizedCallable c, ParameterPosition pos, ParamNode p | - n = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and - p.isParameterOf(c, pos) and - result = getNodeType(p) - ) - } - - /** Holds if summary node `out` contains output of kind `rk` from call `c`. */ - predicate summaryOutNode(DataFlowCall c, Node out, ReturnKind rk) { - exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | - callbackOutput(callable, s, receiver, rk) and - out = summaryNodeInputState(callable, s) and - c = summaryDataFlowCall(receiver) - ) - } - - /** Holds if summary node `arg` is at position `pos` in the call `c`. */ - predicate summaryArgumentNode(DataFlowCall c, Node arg, ArgumentPosition pos) { - exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | - callbackInput(callable, s, receiver, pos) and - arg = summaryNodeOutputState(callable, s) and - c = summaryDataFlowCall(receiver) - ) - } - - /** Holds if summary node `post` is a post-update node with pre-update node `pre`. */ - predicate summaryPostUpdateNode(Node post, Node pre) { - exists(SummarizedCallable c, ParameterPosition pos | - isParameterPostUpdate(post, c, pos) and - pre.(ParamNode).isParameterOf(c, pos) - or - pre = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and - post = summaryNode(c, TSummaryNodeClearsContentState(pos, true)) - ) - or - exists(SummarizedCallable callable, SummaryComponentStack s | - callbackInput(callable, s, _, _) and - pre = summaryNodeOutputState(callable, s) and - post = summaryNodeInputState(callable, s) - ) - } - - /** Holds if summary node `ret` is a return node of kind `rk`. */ - predicate summaryReturnNode(Node ret, ReturnKind rk) { - exists(SummarizedCallable callable, SummaryComponentStack s | - ret = summaryNodeOutputState(callable, s) and - s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk)) - ) - } - - /** - * Holds if flow is allowed to pass from parameter `p`, to a return - * node, and back out to `p`. - */ - predicate summaryAllowParameterReturnInSelf(ParamNode p) { - exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) | - c.clearsContent(ppos, _) - or - exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents | - summary(c, inputContents, outputContents, _) and - inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and - outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) - ) - ) - } - - /** Provides a compilation of flow summaries to atomic data-flow steps. */ - module Steps { - /** - * Holds if there is a local step from `pred` to `succ`, which is synthesized - * from a flow summary. - */ - predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) { - exists( - SummarizedCallable c, SummaryComponentStack inputContents, - SummaryComponentStack outputContents - | - summary(c, inputContents, outputContents, preservesValue) and - pred = summaryNodeInputState(c, inputContents) and - succ = summaryNodeOutputState(c, outputContents) - | - preservesValue = true - or - preservesValue = false and not summary(c, inputContents, outputContents, true) - ) - or - exists(SummarizedCallable c, ParameterPosition pos | - pred.(ParamNode).isParameterOf(c, pos) and - succ = summaryNode(c, TSummaryNodeClearsContentState(pos, _)) and - preservesValue = true - ) - } - - /** - * Holds if there is a read step of content `c` from `pred` to `succ`, which - * is synthesized from a flow summary. - */ - predicate summaryReadStep(Node pred, Content c, Node succ) { - exists(SummarizedCallable sc, SummaryComponentStack s | - pred = summaryNodeInputState(sc, s.drop(1)) and - succ = summaryNodeInputState(sc, s) and - SummaryComponent::content(c) = s.head() - ) - } - - /** - * Holds if there is a store step of content `c` from `pred` to `succ`, which - * is synthesized from a flow summary. - */ - predicate summaryStoreStep(Node pred, Content c, Node succ) { - exists(SummarizedCallable sc, SummaryComponentStack s | - pred = summaryNodeOutputState(sc, s) and - succ = summaryNodeOutputState(sc, s.drop(1)) and - SummaryComponent::content(c) = s.head() - ) - } - - /** - * Holds if values stored inside content `c` are cleared at `n`. `n` is a - * synthesized summary node, so in order for values to be cleared at calls - * to the relevant method, it is important that flow does not pass over - * the argument, either via use-use flow or def-use flow. - * - * Example: - * - * ``` - * a.b = taint; - * a.clearB(); // assume we have a flow summary for `clearB` that clears `b` on the qualifier - * sink(a.b); - * ``` - * - * In the above, flow should not pass from `a` on the first line (or the second - * line) to `a` on the third line. Instead, there will be synthesized flow from - * `a` on line 2 to the post-update node for `a` on that line (via an intermediate - * node where field `b` is cleared). - */ - predicate summaryClearsContent(Node n, Content c) { - exists(SummarizedCallable sc, ParameterPosition pos | - n = summaryNode(sc, TSummaryNodeClearsContentState(pos, true)) and - sc.clearsContent(pos, c) - ) - } - - pragma[noinline] - private predicate viableParam( - DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, ParamNode p - ) { - p.isParameterOf(sc, ppos) and - sc = viableCallable(call) - } - - /** - * Holds if values stored inside content `c` are cleared inside a - * callable to which `arg` is an argument. - * - * In such cases, it is important to prevent use-use flow out of - * `arg` (see comment for `summaryClearsContent`). - */ - predicate summaryClearsContentArg(ArgNode arg, Content c) { - exists(DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos | - argumentPositionMatch(call, arg, ppos) and - viableParam(call, sc, ppos, _) and - sc.clearsContent(ppos, c) - ) - } - - pragma[nomagic] - private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) { - exists(ParameterPosition ppos, SummarizedCallable sc | - argumentPositionMatch(call, arg, ppos) and - viableParam(call, sc, ppos, result) - ) - } - - pragma[nomagic] - private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) { - exists(DataFlowCall call | - result = summaryArgParam0(call, arg) and - out = rk.getAnOutNode(call) - ) - } - - /** - * Holds if `arg` flows to `out` using a simple flow summary, that is, a flow - * summary without reads and stores. - * - * NOTE: This step should not be used in global data-flow/taint-tracking, but may - * be useful to include in the exposed local data-flow/taint-tracking relations. - */ - predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) { - exists(ReturnKindExt rk, ReturnNodeExt ret | - summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and - ret.getKind() = rk - ) - } - - /** - * Holds if there is a read(+taint) of `c` from `arg` to `out` using a - * flow summary. - * - * NOTE: This step should not be used in global data-flow/taint-tracking, but may - * be useful to include in the exposed local data-flow/taint-tracking relations. - */ - predicate summaryGetterStep(ArgNode arg, Content c, Node out) { - exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | - summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and - summaryLocalStep(mid, ret, _) and - ret.getKind() = rk - ) - } - - /** - * Holds if there is a (taint+)store of `arg` into content `c` of `out` using a - * flow summary. - * - * NOTE: This step should not be used in global data-flow/taint-tracking, but may - * be useful to include in the exposed local data-flow/taint-tracking relations. - */ - predicate summarySetterStep(ArgNode arg, Content c, Node out) { - exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | - summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and - summaryStoreStep(mid, c, ret) and - ret.getKind() = rk - ) - } - } - - /** - * Provides a means of translating externally (e.g., CSV) defined flow - * summaries into a `SummarizedCallable`s. - */ - module External { - /** Holds if `spec` is a relevant external specification. */ - private predicate relevantSpec(string spec) { - summaryElement(_, spec, _, _) or - summaryElement(_, _, spec, _) or - sourceElement(_, spec, _) or - sinkElement(_, spec, _) - } - - private class AccessPathRange extends AccessPath::Range { - AccessPathRange() { relevantSpec(this) } - } - - /** Holds if specification component `c` parses as parameter `n`. */ - predicate parseParam(AccessPathToken token, ArgumentPosition pos) { - token.getName() = "Parameter" and - pos = parseParamBody(token.getAnArgument()) - } - - /** Holds if specification component `c` parses as argument `n`. */ - predicate parseArg(AccessPathToken token, ParameterPosition pos) { - token.getName() = "Argument" and - pos = parseArgBody(token.getAnArgument()) - } - - private SummaryComponent interpretComponent(AccessPathToken token) { - exists(ParameterPosition pos | - parseArg(token, pos) and result = SummaryComponent::argument(pos) - ) - or - exists(ArgumentPosition pos | - parseParam(token, pos) and result = SummaryComponent::parameter(pos) - ) - or - token = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind()) - or - result = interpretComponentSpecific(token) - } - - /** - * Holds if `spec` specifies summary component stack `stack`. - */ - predicate interpretSpec(AccessPath spec, SummaryComponentStack stack) { - interpretSpec(spec, spec.getNumToken(), stack) - } - - /** Holds if the first `n` tokens of `spec` resolves to `stack`. */ - private predicate interpretSpec(AccessPath spec, int n, SummaryComponentStack stack) { - n = 1 and - stack = SummaryComponentStack::singleton(interpretComponent(spec.getToken(0))) - or - exists(SummaryComponent head, SummaryComponentStack tail | - interpretSpec(spec, n, head, tail) and - stack = SummaryComponentStack::push(head, tail) - ) - } - - /** Holds if the first `n` tokens of `spec` resolves to `head` followed by `tail` */ - private predicate interpretSpec( - AccessPath spec, int n, SummaryComponent head, SummaryComponentStack tail - ) { - interpretSpec(spec, n - 1, tail) and - head = interpretComponent(spec.getToken(n - 1)) - } - - private class MkStack extends RequiredSummaryComponentStack { - override predicate required(SummaryComponent head, SummaryComponentStack tail) { - interpretSpec(_, _, head, tail) - } - } - - private class SummarizedCallableExternal extends SummarizedCallable { - SummarizedCallableExternal() { summaryElement(this, _, _, _) } - - override predicate propagatesFlow( - SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - ) { - exists(AccessPath inSpec, AccessPath outSpec, string kind | - summaryElement(this, inSpec, outSpec, kind) and - interpretSpec(inSpec, input) and - interpretSpec(outSpec, output) - | - kind = "value" and preservesValue = true - or - kind = "taint" and preservesValue = false - ) - } - } - - /** Holds if component `c` of specification `spec` cannot be parsed. */ - predicate invalidSpecComponent(AccessPath spec, string c) { - c = spec.getToken(_) and - not exists(interpretComponent(c)) - } - - private predicate inputNeedsReference(AccessPathToken c) { - c.getName() = "Argument" or - inputNeedsReferenceSpecific(c) - } - - private predicate outputNeedsReference(AccessPathToken c) { - c.getName() = ["Argument", "ReturnValue"] or - outputNeedsReferenceSpecific(c) - } - - private predicate sourceElementRef(InterpretNode ref, AccessPath output, string kind) { - exists(SourceOrSinkElement e | - sourceElement(e, output, kind) and - if outputNeedsReference(output.getToken(0)) - then e = ref.getCallTarget() - else e = ref.asElement() - ) - } - - private predicate sinkElementRef(InterpretNode ref, AccessPath input, string kind) { - exists(SourceOrSinkElement e | - sinkElement(e, input, kind) and - if inputNeedsReference(input.getToken(0)) - then e = ref.getCallTarget() - else e = ref.asElement() - ) - } - - /** Holds if the first `n` tokens of `output` resolve to the given interpretation. */ - private predicate interpretOutput( - AccessPath output, int n, InterpretNode ref, InterpretNode node - ) { - sourceElementRef(ref, output, _) and - n = 0 and - ( - if output = "" - then - // Allow language-specific interpretation of the empty access path - interpretOutputSpecific("", ref, node) - else node = ref - ) - or - exists(InterpretNode mid, AccessPathToken c | - interpretOutput(output, n - 1, ref, mid) and - c = output.getToken(n - 1) - | - exists(ArgumentPosition apos, ParameterPosition ppos | - node.asNode().(PostUpdateNode).getPreUpdateNode().(ArgNode).argumentOf(mid.asCall(), apos) and - parameterMatch(ppos, apos) - | - c = "Argument" or parseArg(c, ppos) - ) - or - exists(ArgumentPosition apos, ParameterPosition ppos | - node.asNode().(ParamNode).isParameterOf(mid.asCallable(), ppos) and - parameterMatch(ppos, apos) - | - c = "Parameter" or parseParam(c, apos) - ) - or - c = "ReturnValue" and - node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnValueKind())) - or - interpretOutputSpecific(c, mid, node) - ) - } - - /** Holds if the first `n` tokens of `input` resolve to the given interpretation. */ - private predicate interpretInput(AccessPath input, int n, InterpretNode ref, InterpretNode node) { - sinkElementRef(ref, input, _) and - n = 0 and - ( - if input = "" - then - // Allow language-specific interpretation of the empty access path - interpretInputSpecific("", ref, node) - else node = ref - ) - or - exists(InterpretNode mid, AccessPathToken c | - interpretInput(input, n - 1, ref, mid) and - c = input.getToken(n - 1) - | - exists(ArgumentPosition apos, ParameterPosition ppos | - node.asNode().(ArgNode).argumentOf(mid.asCall(), apos) and - parameterMatch(ppos, apos) - | - c = "Argument" or parseArg(c, ppos) - ) - or - exists(ReturnNodeExt ret | - c = "ReturnValue" and - ret = node.asNode() and - ret.getKind().(ValueReturnKind).getKind() = getReturnValueKind() and - mid.asCallable() = getNodeEnclosingCallable(ret) - ) - or - interpretInputSpecific(c, mid, node) - ) - } - - /** - * Holds if `node` is specified as a source with the given kind in a CSV flow - * model. - */ - predicate isSourceNode(InterpretNode node, string kind) { - exists(InterpretNode ref, AccessPath output | - sourceElementRef(ref, output, kind) and - interpretOutput(output, output.getNumToken(), ref, node) - ) - } - - /** - * Holds if `node` is specified as a sink with the given kind in a CSV flow - * model. - */ - predicate isSinkNode(InterpretNode node, string kind) { - exists(InterpretNode ref, AccessPath input | - sinkElementRef(ref, input, kind) and - interpretInput(input, input.getNumToken(), ref, node) - ) - } - } - - /** Provides a query predicate for outputting a set of relevant flow summaries. */ - module TestOutput { - /** A flow summary to include in the `summary/3` query predicate. */ - abstract class RelevantSummarizedCallable extends SummarizedCallable { - /** Gets the string representation of this callable used by `summary/1`. */ - abstract string getCallableCsv(); - - /** Holds if flow is propagated between `input` and `output`. */ - predicate relevantSummary( - SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - ) { - this.propagatesFlow(input, output, preservesValue) - } - } - - /** Render the kind in the format used in flow summaries. */ - private string renderKind(boolean preservesValue) { - preservesValue = true and result = "value" - or - preservesValue = false and result = "taint" - } - - /** - * A query predicate for outputting flow summaries in semi-colon separated format in QL tests. - * The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind", - * ext is hardcoded to empty. - */ - query predicate summary(string csv) { - exists( - RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output, - boolean preservesValue - | - c.relevantSummary(input, output, preservesValue) and - csv = - c.getCallableCsv() + ";;" + getComponentStackCsv(input) + ";" + - getComponentStackCsv(output) + ";" + renderKind(preservesValue) - ) - } - } - - /** - * Provides query predicates for rendering the generated data flow graph for - * a summarized callable. - * - * Import this module into a `.ql` file of `@kind graph` to render the graph. - * The graph is restricted to callables from `RelevantSummarizedCallable`. - */ - module RenderSummarizedCallable { - /** A summarized callable to include in the graph. */ - abstract class RelevantSummarizedCallable extends SummarizedCallable { } - - private newtype TNodeOrCall = - MkNode(Node n) { - exists(RelevantSummarizedCallable c | - n = summaryNode(c, _) - or - n.(ParamNode).isParameterOf(c, _) - ) - } or - MkCall(DataFlowCall call) { - call = summaryDataFlowCall(_) and - call.getEnclosingCallable() instanceof RelevantSummarizedCallable - } - - private class NodeOrCall extends TNodeOrCall { - Node asNode() { this = MkNode(result) } - - DataFlowCall asCall() { this = MkCall(result) } - - string toString() { - result = this.asNode().toString() - or - result = this.asCall().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - query predicate nodes(NodeOrCall n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) { - exists(boolean preservesValue | - Private::Steps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue) and - if preservesValue = true then value = "value" else value = "taint" - ) - or - exists(Content c | - Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and - value = "read (" + c + ")" - or - Private::Steps::summaryStoreStep(a.asNode(), c, b.asNode()) and - value = "store (" + c + ")" - or - Private::Steps::summaryClearsContent(a.asNode(), c) and - b = a and - value = "clear (" + c + ")" - ) - or - summaryPostUpdateNode(b.asNode(), a.asNode()) and - value = "post-update" - or - b.asCall() = summaryDataFlowCall(a.asNode()) and - value = "receiver" - or - exists(ArgumentPosition pos | - summaryArgumentNode(b.asCall(), a.asNode(), pos) and - value = "argument (" + pos + ")" - ) - } - - query predicate edges(NodeOrCall a, NodeOrCall b, string key, string value) { - key = "semmle.label" and - value = strictconcat(string s | edgesComponent(a, b, s) | s, " / ") - } - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll b/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll deleted file mode 100644 index 4d8e19461..000000000 --- a/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll +++ /dev/null @@ -1,299 +0,0 @@ -/** - * Provides Go-specific classes and predicates for defining flow summaries. - */ - -private import go -private import DataFlowPrivate -private import DataFlowUtil -private import FlowSummaryImpl::Private -private import FlowSummaryImpl::Public -private import semmle.go.dataflow.ExternalFlow -private import DataFlowImplCommon - -private module FlowSummaries { - private import semmle.go.dataflow.FlowSummary as F -} - -/** Holds if `i` is a valid parameter position. */ -predicate parameterPosition(int i) { - i = [-1 .. any(DataFlowCallable c).getType().getNumParameter()] -} - -/** Gets the parameter position of the instance parameter. */ -int instanceParameterPosition() { result = -1 } - -/** A parameter position represented by an integer. */ -class ParameterPosition extends int { - ParameterPosition() { parameterPosition(this) } -} - -/** An argument position represented by an integer. */ -class ArgumentPosition extends int { - ArgumentPosition() { parameterPosition(this) } -} - -/** Holds if arguments at position `apos` match parameters at position `ppos`. */ -pragma[inline] -predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** Gets the textual representation of a parameter position in the format used for flow summaries. */ -string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString() } - -/** Gets the textual representation of an argument position in the format used for flow summaries. */ -string getArgumentPositionCsv(ArgumentPosition pos) { result = pos.toString() } - -Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = getSummaryNode(c, state) } - -/** Gets the synthesized data-flow call for `receiver`. */ -DataFlowCall summaryDataFlowCall(Node receiver) { - // We do not currently have support for callback-based library models. - none() -} - -/** Gets the type of content `c`. */ -DataFlowType getContentType(Content c) { result = c.getType() } - -/** Gets the return type of kind `rk` for callable `c`. */ -DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { - result = c.getType().getResultType(rk.getIndex()) -} - -/** - * Gets the type of the `i`th parameter in a synthesized call that targets a - * callback of type `t`. - */ -DataFlowType getCallbackParameterType(DataFlowType t, int i) { none() } - -/** - * Gets the return type of kind `rk` in a synthesized call that targets a - * callback of type `t`. - */ -DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { none() } - -/** - * Holds if an external flow summary exists for `c` with input specification - * `input`, output specification `output`, and kind `kind`. - */ -predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { - exists( - string namespace, string type, boolean subtypes, string name, string signature, string ext - | - summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and - c.asFunction() = interpretElement(namespace, type, subtypes, name, signature, ext).asEntity() - ) -} - -/** Gets the summary component for specification component `c`, if any. */ -bindingset[c] -SummaryComponent interpretComponentSpecific(string c) { - exists(int pos | parseReturn(c, pos) and result = SummaryComponent::return(getReturnKind(pos))) - or - exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content)) -} - -/** Gets the summary component for specification component `c`, if any. */ -private string getContentSpecificCsv(Content c) { - exists(Field f, string package, string className, string fieldName | - f = c.(FieldContent).getField() and - f.hasQualifiedName(package, className, fieldName) and - result = "Field[" + package + "." + className + "." + fieldName + "]" - ) - or - exists(SyntheticField f | - f = c.(SyntheticFieldContent).getField() and result = "SyntheticField[" + f + "]" - ) - or - c instanceof ArrayContent and result = "ArrayElement" - or - c instanceof CollectionContent and result = "Element" - or - c instanceof MapKeyContent and result = "MapKey" - or - c instanceof MapValueContent and result = "MapValue" -} - -/** Gets the textual representation of the content in the format used for flow summaries. */ -string getComponentSpecificCsv(SummaryComponent sc) { - exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecificCsv(c)) - or - exists(ReturnKind rk, int n | n = rk.getIndex() | - sc = TReturnSummaryComponent(rk) and - result = "ReturnValue[" + n + "]" and - n != 0 - ) -} - -/** Holds if input specification component `c` needs a reference. */ -predicate inputNeedsReferenceSpecific(string c) { none() } - -/** Holds if output specification component `c` needs a reference. */ -predicate outputNeedsReferenceSpecific(string c) { parseReturn(c, _) } - -private newtype TSourceOrSinkElement = - TEntityElement(Entity e) or - TAstElement(AstNode n) - -/** An element representable by CSV modeling. */ -class SourceOrSinkElement extends TSourceOrSinkElement { - /** Gets this source or sink element as an entity, if it is one. */ - Entity asEntity() { this = TEntityElement(result) } - - /** Gets this source or sink element as an AST node, if it is one. */ - AstNode asAstNode() { this = TAstElement(result) } - - /** Gets a textual representation of this source or sink element. */ - string toString() { - result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()] - } - - predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) { - this.asEntity().hasLocationInfo(fp, sl, sc, el, ec) or - this.asAstNode().hasLocationInfo(fp, sl, sc, el, ec) - } -} - -/** - * Holds if an external source specification exists for `e` with output specification - * `output` and kind `kind`. - */ -predicate sourceElement(SourceOrSinkElement e, string output, string kind) { - exists( - string namespace, string type, boolean subtypes, string name, string signature, string ext - | - sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and - e = interpretElement(namespace, type, subtypes, name, signature, ext) - ) -} - -/** - * Holds if an external sink specification exists for `e` with input specification - * `input` and kind `kind`. - */ -predicate sinkElement(SourceOrSinkElement e, string input, string kind) { - exists( - string namespace, string type, boolean subtypes, string name, string signature, string ext - | - sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and - e = interpretElement(namespace, type, subtypes, name, signature, ext) - ) -} - -/** Gets the return kind corresponding to specification `"ReturnValue"`. */ -ReturnKind getReturnValueKind() { result = getReturnKind(0) } - -private newtype TInterpretNode = - TElement(SourceOrSinkElement 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() { result = this.asElement().asAstNode() } - - /** Gets the callable that this node corresponds to, if any. */ - DataFlowCallable asCallable() { - result.asFunction() = this.asElement().asEntity() - or - result.asFuncLit() = this.asElement().asAstNode() - } - - /** Gets the target of this call, if any. */ - SourceOrSinkElement getCallTarget() { - result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget() - } - - /** Gets a textual representation of this node. */ - string toString() { - result = this.asElement().toString() - or - result = this.asNode().toString() - } - - /** Gets the location of this node. */ - predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) { - this.asElement().hasLocationInfo(fp, sl, sc, el, ec) - or - this.asNode().hasLocationInfo(fp, sl, sc, el, ec) - } -} - -/** Provides additional sink specification logic required for annotations. */ -pragma[inline] -predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { - exists(int pos | node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnKind(pos))) | - parseReturn(c, pos) - ) - or - exists(Node n, SourceOrSinkElement e | - n = node.asNode() and - e = mid.asElement() - | - (c = "Parameter" or c = "") and - node.asNode().asParameter() = e.asEntity() - or - c = "" and - n.(DataFlow::FieldReadNode).getField() = e.asEntity() - ) -} - -/** Provides additional source specification logic required for annotations. */ -pragma[inline] -predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) { - exists(int pos, ReturnNodeExt ret | - parseReturn(c, pos) and - ret = n.asNode() and - ret.getKind().(ValueReturnKind).getKind() = getReturnKind(pos) and - mid.asCallable() = getNodeEnclosingCallable(ret) - ) - or - exists(DataFlow::Write fw, Field f | - c = "" and - f = mid.asElement().asEntity() and - fw.writesField(_, f, n.asNode()) - ) -} - -/** Holds if specification component `c` parses as return value `n`. */ -predicate parseReturn(AccessPathToken c, int n) { - ( - c = "ReturnValue" and n = 0 - or - c.getName() = "ReturnValue" and - n = parseConstantOrRange(c.getAnArgument()) - ) -} - -bindingset[arg] -private int parseConstantOrRange(string arg) { - result = arg.toInt() - or - exists(int n1, int n2 | - arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 1).toInt() = n1 and - arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 2).toInt() = n2 and - result = [n1 .. n2] - ) -} - -bindingset[arg] -ArgumentPosition parseParamBody(string arg) { result = parseConstantOrRange(arg) } - -bindingset[arg] -ParameterPosition parseArgBody(string arg) { result = parseConstantOrRange(arg) } diff --git a/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll b/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll deleted file mode 100644 index f33048ed0..000000000 --- a/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Provides Go-specific definitions for use in the taint-tracking library. - */ - -private import go -private import FlowSummaryImpl as FlowSummaryImpl - -/** - * Holds if taint can flow from `src` to `sink` in zero or more - * local (intra-procedural) steps. - */ -predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) } - -/** - * Holds if taint can flow from `src` to `sink` in zero or more - * local (intra-procedural) steps. - */ -predicate localExprTaint(Expr src, Expr sink) { - localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink)) -} - -/** - * Holds if taint can flow in one local step from `src` to `sink`. - */ -predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { - DataFlow::localFlowStep(src, sink) or - localAdditionalTaintStep(src, sink) or - // Simple flow through library code is included in the exposed local - // step relation, even though flow is technically inter-procedural - FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false) -} - -private Type getElementType(Type containerType) { - result = containerType.(ArrayType).getElementType() or - result = containerType.(SliceType).getElementType() or - result = containerType.(ChanType).getElementType() or - result = containerType.(MapType).getValueType() or - result = containerType.(PointerType).getPointerType() -} - -/** - * Holds if default `TaintTracking::Configuration`s should allow implicit reads - * of `c` at sinks and inputs to additional taint steps. - */ -bindingset[node] -predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { - exists(Type containerType | - node instanceof DataFlow::ArgumentNode and - getElementType*(node.getType()) = containerType - | - containerType instanceof ArrayType and - c instanceof DataFlow::ArrayContent - or - containerType instanceof SliceType and - c instanceof DataFlow::ArrayContent - or - containerType instanceof ChanType and - c instanceof DataFlow::CollectionContent - or - containerType instanceof MapType and - c instanceof DataFlow::MapValueContent - or - c.(DataFlow::PointerContent).getPointerType() = containerType - ) -} - -private newtype TUnit = TMkUnit() - -/** A singleton class containing a single dummy "unit" value. */ -private class Unit extends TUnit { - /** Gets a textual representation of this element. */ - string toString() { result = "unit" } -} - -/** - * A unit class for adding additional taint steps. - * - * Extend this class to add additional taint steps that should apply to all - * taint configurations. - */ -class AdditionalTaintStep extends Unit { - /** - * Holds if the step from `node1` to `node2` should be considered a taint - * step for all configurations. - */ - abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); -} - -/** - * Holds if the additional step from `pred` to `succ` should be included in all - * global taint flow configurations. - */ -predicate localAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - referenceStep(pred, succ) or - elementWriteStep(pred, succ) or - fieldReadStep(pred, succ) or - elementStep(pred, succ) or - tupleStep(pred, succ) or - stringConcatStep(pred, succ) or - sliceStep(pred, succ) or - any(FunctionModel fm).taintStep(pred, succ) or - any(AdditionalTaintStep a).step(pred, succ) or - FlowSummaryImpl::Private::Steps::summaryLocalStep(pred, succ, false) -} - -/** - * Holds if taint flows from `pred` to `succ` via a reference or dereference. - * - * The taint-tracking library does not distinguish between a reference and its referent, - * treating one as tainted if the other is. - */ -predicate referenceStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::AddressOperationNode addr | - // from `x` to `&x` - pred = addr.getOperand() and - succ = addr - or - // from `&x` to `x` - pred = addr and - succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = addr.getOperand() - ) - or - exists(DataFlow::PointerDereferenceNode deref | - // from `x` to `*x` - pred = deref.getOperand() and - succ = deref - or - // from `*x` to `x` - pred = deref and - succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = deref.getOperand() - ) -} - -/** - * Holds if there is an assignment of the form `succ[idx] = pred`, meaning that `pred` may taint - * `succ`. - */ -predicate elementWriteStep(DataFlow::Node pred, DataFlow::Node succ) { - any(DataFlow::Write w).writesElement(succ.(DataFlow::PostUpdateNode).getPreUpdateNode(), _, pred) -} - -/** Holds if taint flows from `pred` to `succ` via a field read. */ -predicate fieldReadStep(DataFlow::Node pred, DataFlow::Node succ) { - succ.(DataFlow::FieldReadNode).getBase() = pred -} - -/** - * Holds if taint flows from `pred` to `succ` via an array, map, slice, or string - * index operation. - */ -predicate elementStep(DataFlow::Node pred, DataFlow::Node succ) { - succ.(DataFlow::ElementReadNode).getBase() = pred - or - exists(IR::GetNextEntryInstruction nextEntry | - pred.asInstruction() = nextEntry.getDomain() and - // only step into the value, not the index - succ.asInstruction() = IR::extractTupleElement(nextEntry, 1) - ) -} - -deprecated predicate arrayStep = elementStep/2; - -/** Holds if taint flows from `pred` to `succ` via an extract tuple operation. */ -predicate tupleStep(DataFlow::Node pred, DataFlow::Node succ) { - succ = DataFlow::extractTupleElement(pred, _) -} - -/** Holds if taint flows from `pred` to `succ` via string concatenation. */ -predicate stringConcatStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::BinaryOperationNode conc | - conc.getOperator() = "+" and conc.getType() instanceof StringType - | - succ = conc and conc.getAnOperand() = pred - ) -} - -/** Holds if taint flows from `pred` to `succ` via a slice operation. */ -predicate sliceStep(DataFlow::Node pred, DataFlow::Node succ) { - succ.(DataFlow::SliceNode).getBase() = pred -} - -/** - * A model of a function specifying that the function propagates taint from - * a parameter or qualifier to a result. - */ -abstract class FunctionModel extends Function { - /** Holds if taint propagates through this function from `input` to `output`. */ - abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output); - - /** Gets an input node for this model for the call `c`. */ - DataFlow::Node getAnInputNode(DataFlow::CallNode c) { this.taintStepForCall(result, _, c) } - - /** Gets an output node for this model for the call `c`. */ - DataFlow::Node getAnOutputNode(DataFlow::CallNode c) { this.taintStepForCall(_, result, c) } - - /** Holds if this function model causes taint to flow from `pred` to `succ` for the call `c`. */ - predicate taintStepForCall(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode c) { - c = this.getACall() and - exists(FunctionInput inp, FunctionOutput outp | this.hasTaintFlow(inp, outp) | - pred = inp.getNode(c) and - succ = outp.getNode(c) - ) - } - - /** Holds if this function model causes taint to flow from `pred` to `succ`. */ - predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) { - this.taintStepForCall(pred, succ, _) - } -} - -/** - * 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) { - localAdditionalTaintStep(src, sink) -} - -/** - * A sanitizer in all global taint flow configurations but not in local taint. - */ -abstract class DefaultTaintSanitizer extends DataFlow::Node { } - -/** - * Holds if `node` should be a sanitizer in all global taint flow configurations - * but not in local taint. - */ -predicate defaultTaintSanitizer(DataFlow::Node node) { node instanceof DefaultTaintSanitizer } - -/** - * A sanitizer guard in all global taint flow configurations but not in local taint. - */ -abstract class DefaultTaintSanitizerGuard extends DataFlow::BarrierGuard { } - -/** - * Holds if `guard` should be a sanitizer guard in all global taint flow configurations - * but not in local taint. - */ -predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { - guard instanceof DefaultTaintSanitizerGuard -} - -/** - * An equality test acting as a sanitizer guard for `nonConstNode` by - * restricting it to a known value. - * - * Note that comparisons to `nil` are excluded. This is needed for performance - * reasons. - */ -class EqualityTestGuard extends DefaultTaintSanitizerGuard, DataFlow::EqualityTestNode { - DataFlow::Node nonConstNode; - - EqualityTestGuard() { - this.getAnOperand().isConst() and - nonConstNode = this.getAnOperand() and - not nonConstNode.isConst() and - not this.getAnOperand() = Builtin::nil().getARead() - } - - override predicate checks(Expr e, boolean outcome) { - e = nonConstNode.asExpr() and - outcome = this.getPolarity() - } -} - -/** - * Holds if data flows from `node` to `switchExprNode`, which is the expression - * of a switch statement. - */ -private predicate flowsToSwitchExpression(DataFlow::Node node, DataFlow::Node switchExprNode) { - switchExprNode.asExpr() = any(ExpressionSwitchStmt ess).getExpr() and - DataFlow::localFlow(node, switchExprNode) -} - -/** - * Holds if `inputNode` is the exit node of a parameter to `fd` and data flows - * from `inputNode` to the expression of a switch statement. - */ -private predicate isPossibleInputNode(DataFlow::Node inputNode, FuncDef fd) { - inputNode = any(FunctionInput inp | inp.isParameter(_)).getExitNode(fd) and - flowsToSwitchExpression(inputNode, _) -} - -/** - * Gets a predecessor of `succ` without following edges corresponding to - * passing a constant case test in a switch statement which is switching on - * an expression which data flows to from `inputNode`. - */ -private ControlFlow::Node getANonTestPassingPredecessor( - ControlFlow::Node succ, DataFlow::Node inputNode -) { - isPossibleInputNode(inputNode, succ.getRoot()) and - result = succ.getAPredecessor() and - not exists(Expr testExpr, DataFlow::Node switchExprNode | - flowsToSwitchExpression(inputNode, switchExprNode) and - ControlFlow::isSwitchCaseTestPassingEdge(result, succ, switchExprNode.asExpr(), testExpr) and - testExpr.isConst() - ) -} - -private ControlFlow::Node getANonTestPassingReachingNodeRecursive( - ControlFlow::Node n, DataFlow::Node inputNode -) { - isPossibleInputNode(inputNode, n.getRoot()) and - ( - result = n or - result = - getANonTestPassingReachingNodeRecursive(getANonTestPassingPredecessor(n, inputNode), inputNode) - ) -} - -/** - * Gets a node by following predecessors from `ret` without following edges - * corresponding to passing a constant case test in a switch statement which is - * switching on an expression which data flows to from `inputNode`. - */ -private ControlFlow::Node getANonTestPassingReachingNodeBase( - IR::ReturnInstruction ret, DataFlow::Node inputNode -) { - result = getANonTestPassingReachingNodeRecursive(ret, inputNode) -} - -/** - * Holds if every way to get from the entry node of the function to `ret` - * involves passing a constant test case in a switch statement which is - * switching on an expression which data flows to from `inputNode`. - */ -private predicate mustPassConstantCaseTestToReach( - IR::ReturnInstruction ret, DataFlow::Node inputNode -) { - isPossibleInputNode(inputNode, ret.getRoot()) and - not exists(ControlFlow::Node entry | entry = ret.getRoot().getEntryNode() | - entry = getANonTestPassingReachingNodeBase(ret, inputNode) - ) -} - -/** - * Holds if whenever `outp` of function `f` satisfies `p`, the input `inp` of - * `f` matched a constant in a case clause of a switch statement. - * - * We check this by looking for guards on `inp` that collectively dominate all - * the `return` statements in `f` that can return `true`. This means that if - * `f` returns `true`, one of the guards must have been satisfied. (Similar - * reasoning is applied for statements returning `false`, `nil` or a non-`nil` - * value.) - */ -predicate functionEnsuresInputIsConstant( - Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p -) { - exists(FuncDecl fd | fd.getFunction() = f | - exists(boolean b | - p.isBoolean(b) and - forex(DataFlow::Node ret, IR::ReturnInstruction ri | - ret = outp.getEntryNode(fd) and - ri.getReturnStmt().getAnExpr() = ret.asExpr() and - DataFlow::possiblyReturnsBool(fd, outp, ret, b) - | - mustPassConstantCaseTestToReach(ri, inp.getExitNode(fd)) - ) - ) - or - p.isNonNil() and - forex(DataFlow::Node ret, IR::ReturnInstruction ri | - ret = outp.getEntryNode(fd) and - ri.getReturnStmt().getAnExpr() = ret.asExpr() and - DataFlow::possiblyReturnsNonNil(fd, outp, ret) - | - mustPassConstantCaseTestToReach(ri, inp.getExitNode(fd)) - ) - or - p.isNil() and - forex(DataFlow::Node ret, IR::ReturnInstruction ri | - ret = outp.getEntryNode(fd) and - ri.getReturnStmt().getAnExpr() = ret.asExpr() and - ret.asExpr() = Builtin::nil().getAReference() - | - exists(DataFlow::Node exprNode | - DataFlow::localFlow(inp.getExitNode(fd), exprNode) and - mustPassConstantCaseTestToReach(ri, inp.getExitNode(fd)) - ) - ) - ) -} - -/** - * Holds if whenever `outputNode` satisfies `p`, `inputNode` matched a constant - * in a case clause of a switch statement. - */ -pragma[noinline] -predicate inputIsConstantIfOutputHasProperty( - DataFlow::Node inputNode, DataFlow::Node outputNode, DataFlow::Property p -) { - exists(Function f, FunctionInput inp, FunctionOutput outp, DataFlow::CallNode call | - functionEnsuresInputIsConstant(f, inp, outp, p) and - call = f.getACall() and - inputNode = inp.getNode(call) and - DataFlow::localFlow(outp.getNode(call), outputNode) - ) -} - -/** - * A comparison against a list of constants, acting as a sanitizer guard for - * `guardedExpr` by restricting it to a known value. - * - * Currently this only looks for functions containing a switch statement, but - * it could equally look for a check for membership of a constant map or - * constant array, which does not need to be in its own function. - */ -class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizerGuard { - DataFlow::Node guardedExpr; - boolean outcome; - - ListOfConstantsComparisonSanitizerGuard() { - exists(DataFlow::Node outputNode, DataFlow::Property p | - inputIsConstantIfOutputHasProperty(guardedExpr, outputNode, p) and - p.checkOn(this, outcome, outputNode) - ) - } - - override predicate checks(Expr e, boolean branch) { - e = guardedExpr.asExpr() and branch = outcome - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll deleted file mode 100644 index 7f7d5bbb8..000000000 --- a/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) taint tracking. - * This file re-exports the local (intraprocedural) taint-tracking analysis - * from `TaintTrackingParameter::Public` and adds a global analysis, mainly - * exposed through the `Configuration` class. For some languages, this file - * exists in several identical copies, allowing queries to use multiple - * `Configuration` classes that depend on each other without introducing - * mutual recursion among those configurations. - */ - -import TaintTrackingParameter::Public -private import TaintTrackingParameter::Private - -/** - * A configuration of interprocedural taint tracking analysis. This defines - * sources, sinks, and any other configurable aspect of the analysis. Each - * use of the taint tracking library must define its own unique extension of - * this abstract class. - * - * A taint-tracking configuration is a special data flow configuration - * (`DataFlow::Configuration`) that allows for flow through nodes that do not - * necessarily preserve values but are still relevant from a taint tracking - * perspective. (For example, string concatenation, where one of the operands - * is tainted.) - * - * To create a configuration, extend this class with a subclass whose - * characteristic predicate is a unique singleton string. For example, write - * - * ```ql - * class MyAnalysisConfiguration extends TaintTracking::Configuration { - * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } - * // Override `isSource` and `isSink`. - * // Optionally override `isSanitizer`. - * // Optionally override `isSanitizerIn`. - * // Optionally override `isSanitizerOut`. - * // Optionally override `isSanitizerGuard`. - * // Optionally override `isAdditionalTaintStep`. - * } - * ``` - * - * Then, to query whether there is flow between some `source` and `sink`, - * write - * - * ```ql - * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) - * ``` - * - * Multiple configurations can coexist, but it is unsupported to depend on - * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the - * overridden predicates that define sources, sinks, or additional steps. - * Instead, the dependency should go to a `TaintTracking2::Configuration` or a - * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc. - */ -abstract class Configuration extends DataFlow::Configuration { - bindingset[this] - Configuration() { any() } - - /** - * Holds if `source` is a relevant taint source. - * - * The smaller this predicate is, the faster `hasFlow()` will converge. - */ - // overridden to provide taint-tracking specific qldoc - abstract override predicate isSource(DataFlow::Node source); - - /** - * Holds if `sink` is a relevant taint sink. - * - * The smaller this predicate is, the faster `hasFlow()` will converge. - */ - // overridden to provide taint-tracking specific qldoc - abstract override predicate isSink(DataFlow::Node sink); - - /** Holds if the node `node` is a taint sanitizer. */ - predicate isSanitizer(DataFlow::Node node) { none() } - - final override predicate isBarrier(DataFlow::Node node) { - this.isSanitizer(node) or - defaultTaintSanitizer(node) - } - - /** Holds if taint propagation into `node` is prohibited. */ - predicate isSanitizerIn(DataFlow::Node node) { none() } - - final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } - - /** Holds if taint propagation out of `node` is prohibited. */ - predicate isSanitizerOut(DataFlow::Node node) { none() } - - final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } - - /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ - predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { - this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard) - } - - /** - * Holds if the additional taint propagation step from `node1` to `node2` - * must be taken into account in the analysis. - */ - predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } - - final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - this.isAdditionalTaintStep(node1, node2) or - defaultAdditionalTaintStep(node1, node2) - } - - override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) { - (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and - defaultImplicitTaintRead(node, c) - } - - /** - * Holds if taint may flow from `source` to `sink` for this configuration. - */ - // overridden to provide taint-tracking specific qldoc - override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) { - super.hasFlow(source, sink) - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingParameter.qll deleted file mode 100644 index b38482194..000000000 --- a/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ /dev/null @@ -1,5 +0,0 @@ -import semmle.go.dataflow.internal.TaintTrackingUtil as Public - -module Private { - import semmle.go.dataflow.DataFlow::DataFlow as DataFlow -} diff --git a/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll deleted file mode 100644 index 7f7d5bbb8..000000000 --- a/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) taint tracking. - * This file re-exports the local (intraprocedural) taint-tracking analysis - * from `TaintTrackingParameter::Public` and adds a global analysis, mainly - * exposed through the `Configuration` class. For some languages, this file - * exists in several identical copies, allowing queries to use multiple - * `Configuration` classes that depend on each other without introducing - * mutual recursion among those configurations. - */ - -import TaintTrackingParameter::Public -private import TaintTrackingParameter::Private - -/** - * A configuration of interprocedural taint tracking analysis. This defines - * sources, sinks, and any other configurable aspect of the analysis. Each - * use of the taint tracking library must define its own unique extension of - * this abstract class. - * - * A taint-tracking configuration is a special data flow configuration - * (`DataFlow::Configuration`) that allows for flow through nodes that do not - * necessarily preserve values but are still relevant from a taint tracking - * perspective. (For example, string concatenation, where one of the operands - * is tainted.) - * - * To create a configuration, extend this class with a subclass whose - * characteristic predicate is a unique singleton string. For example, write - * - * ```ql - * class MyAnalysisConfiguration extends TaintTracking::Configuration { - * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } - * // Override `isSource` and `isSink`. - * // Optionally override `isSanitizer`. - * // Optionally override `isSanitizerIn`. - * // Optionally override `isSanitizerOut`. - * // Optionally override `isSanitizerGuard`. - * // Optionally override `isAdditionalTaintStep`. - * } - * ``` - * - * Then, to query whether there is flow between some `source` and `sink`, - * write - * - * ```ql - * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) - * ``` - * - * Multiple configurations can coexist, but it is unsupported to depend on - * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the - * overridden predicates that define sources, sinks, or additional steps. - * Instead, the dependency should go to a `TaintTracking2::Configuration` or a - * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc. - */ -abstract class Configuration extends DataFlow::Configuration { - bindingset[this] - Configuration() { any() } - - /** - * Holds if `source` is a relevant taint source. - * - * The smaller this predicate is, the faster `hasFlow()` will converge. - */ - // overridden to provide taint-tracking specific qldoc - abstract override predicate isSource(DataFlow::Node source); - - /** - * Holds if `sink` is a relevant taint sink. - * - * The smaller this predicate is, the faster `hasFlow()` will converge. - */ - // overridden to provide taint-tracking specific qldoc - abstract override predicate isSink(DataFlow::Node sink); - - /** Holds if the node `node` is a taint sanitizer. */ - predicate isSanitizer(DataFlow::Node node) { none() } - - final override predicate isBarrier(DataFlow::Node node) { - this.isSanitizer(node) or - defaultTaintSanitizer(node) - } - - /** Holds if taint propagation into `node` is prohibited. */ - predicate isSanitizerIn(DataFlow::Node node) { none() } - - final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } - - /** Holds if taint propagation out of `node` is prohibited. */ - predicate isSanitizerOut(DataFlow::Node node) { none() } - - final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } - - /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ - predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { - this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard) - } - - /** - * Holds if the additional taint propagation step from `node1` to `node2` - * must be taken into account in the analysis. - */ - predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } - - final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - this.isAdditionalTaintStep(node1, node2) or - defaultAdditionalTaintStep(node1, node2) - } - - override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) { - (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and - defaultImplicitTaintRead(node, c) - } - - /** - * Holds if taint may flow from `source` to `sink` for this configuration. - */ - // overridden to provide taint-tracking specific qldoc - override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) { - super.hasFlow(source, sink) - } -} diff --git a/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingParameter.qll b/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingParameter.qll deleted file mode 100644 index 1130c2e42..000000000 --- a/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingParameter.qll +++ /dev/null @@ -1,5 +0,0 @@ -import semmle.go.dataflow.internal.TaintTrackingUtil as Public - -module Private { - import semmle.go.dataflow.DataFlow2::DataFlow2 as DataFlow -} diff --git a/ql/lib/semmle/go/dependencies/Dependencies.qll b/ql/lib/semmle/go/dependencies/Dependencies.qll deleted file mode 100644 index 5fc746e69..000000000 --- a/ql/lib/semmle/go/dependencies/Dependencies.qll +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Provides classes for modeling go.mod dependencies. - */ - -import go - -/** - * An abstract representation of a dependency. - */ -abstract class Dependency extends Locatable { - /** - * Holds if this dependency has package path `path` and version `v`. - * - * If the version cannot be determined, `v` is bound to the string - * `"unknown"`. - */ - abstract predicate info(string path, string v); - - /** Gets the package path of this dependency. */ - string getDepPath() { this.info(result, _) } - - /** Gets the version of this dependency. */ - string getDepVersion() { this.info(_, result) } - - /** - * Holds if this dependency is relevant for imports in file `file`. That is, an import of this - * dependency's path that is in `file` will use this dependency. - */ - abstract predicate relevantForFile(File file); - - /** - * An import of this dependency. - */ - ImportSpec getAnImport() { - result.getPath().regexpMatch("\\Q" + this.getDepPath() + "\\E(/.*)?") and - this.relevantForFile(result.getFile()) - } -} - -/** - * A dependency from a go.mod file. - */ -class GoModDependency extends Dependency, GoModRequireLine { - override predicate info(string path, string v) { - this.replacementInfo(path, v) - or - not this.replacementInfo(_, _) and - this.originalInfo(path, v) - } - - override predicate relevantForFile(File file) { - exists(Folder parent | parent.getAFile() = this.getFile() | - parent.getAFolder*().getAFile() = file - ) - } - - /** - * Holds if there is a replace line that replaces this dependency with a dependency on `path`, - * version `v`. - */ - predicate replacementInfo(string path, string v) { - exists(GoModReplaceLine replace | - replace.getFile() = this.getFile() and - replace.getOriginalPath() = this.getPath() - | - path = replace.getReplacementPath() and - ( - v = replace.getReplacementVersion() - or - not exists(replace.getReplacementVersion()) and - v = "unknown" - ) - ) - } - - /** - * Get a version that was excluded for this dependency. - */ - string getAnExcludedVersion() { - exists(GoModExcludeLine exclude | - exclude.getFile() = this.getFile() and - exclude.getPath() = this.getPath() - | - result = exclude.getVersion() - ) - } - - /** - * Holds if this require line originally states dependency `path` had version `ver`. - * - * The actual info of this dependency can change based on `replace` directives in the same go.mod - * file, which replace a dependency with another one. - */ - predicate originalInfo(string path, string v) { path = this.getPath() and v = this.getVersion() } -} diff --git a/ql/lib/semmle/go/dependencies/SemVer.qll b/ql/lib/semmle/go/dependencies/SemVer.qll deleted file mode 100644 index 88d375639..000000000 --- a/ql/lib/semmle/go/dependencies/SemVer.qll +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Provides classes for dealing with semantic versions, for dependency versions. - */ - -import semmle.go.dependencies.Dependencies - -/** - * A SemVer-formatted version string in a dependency. - * - * Pre-release information and build metadata is not yet supported. - */ -class DependencySemVer extends string { - Dependency dep; - string normalized; - - DependencySemVer() { - this = dep.getDepVersion() and - normalized = normalizeSemver(this) - } - - /** - * Holds if this version may be before `last`. - */ - bindingset[last] - predicate maybeBefore(string last) { normalized < normalizeSemver(last) } - - /** - * Holds if this version may be after `first`. - */ - bindingset[first] - predicate maybeAfter(string first) { normalizeSemver(first) < normalized } - - /** - * Holds if this version may be between `first` (inclusive) and `last` (exclusive). - */ - bindingset[first, last] - predicate maybeBetween(string first, string last) { - normalizeSemver(first) <= normalized and - normalized < normalizeSemver(last) - } - - /** - * Holds if this version is equivalent to `other`. - */ - bindingset[other] - predicate is(string other) { normalized = normalizeSemver(other) } - - /** - * Gets the dependency that uses this string. - */ - Dependency getDependency() { result = dep } -} - -bindingset[str] -private string leftPad(string str) { result = ("000" + str).suffix(str.length()) } - -/** - * Normalizes a SemVer string such that the lexicographical ordering - * of two normalized strings is consistent with the SemVer ordering. - * - * Pre-release information and build metadata is not yet supported. - */ -bindingset[orig] -private string normalizeSemver(string orig) { - exists(string pattern, string major, string minor, string patch | - pattern = "v?(\\d+)\\.(\\d+)\\.(\\d+)(\\D.*)?" and - major = orig.regexpCapture(pattern, 1) and - minor = orig.regexpCapture(pattern, 2) and - patch = orig.regexpCapture(pattern, 3) - | - result = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch) - ) -} - -/** - * A version string in a dependency that has a SemVer, but also contains a git commit SHA. - * - * This class is useful for interacting with go.mod versions, which use SemVer, but can also contain - * SHAs if no useful tags are found, or when a user wishes to specify a commit SHA. - * - * Pre-release information and build metadata is not yet supported. - */ -class DependencySemShaVersion extends DependencySemVer { - string sha; - - DependencySemShaVersion() { sha = this.regexpCapture(".*-([0-9a-f]+)", 1) } - - /** - * Gets the commit SHA associated with this version. - */ - string getSha() { result = sha } - - bindingset[other] - override predicate is(string other) { - this.getSha() = other.(DependencySemShaVersion).getSha() - or - not other instanceof DependencySemShaVersion and - super.is(other) - } -} diff --git a/ql/lib/semmle/go/frameworks/Beego.qll b/ql/lib/semmle/go/frameworks/Beego.qll deleted file mode 100644 index edcf383c6..000000000 --- a/ql/lib/semmle/go/frameworks/Beego.qll +++ /dev/null @@ -1,378 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources, sinks and taint propagators - * from the `github.com/beego/beego` package. - */ - -import go -import semmle.go.security.Xss -private import semmle.go.security.SafeUrlFlowCustomizations - -/** - * Provides classes for working with untrusted flow sources, sinks and taint propagators - * from the [Beego](https://github.com/beego/beego) package. - */ -module Beego { - /** Gets the v1 module path `github.com/astaxie/beego` or `github.com/beego/beego`. */ - string v1modulePath() { result = ["github.com/astaxie/beego", "github.com/beego/beego"] } - - /** Gets the v2 module path `github.com/beego/beego/v2` */ - string v2modulePath() { result = "github.com/beego/beego/v2" } - - /** Gets the path for the root package of beego. */ - string packagePath() { - result = package(v1modulePath(), "") - or - result = package(v2modulePath(), "server/web") - } - - /** Gets the path for the context package of beego. */ - string contextPackagePath() { - result = package(v1modulePath(), "context") - or - result = package(v2modulePath(), "server/web/context") - } - - /** Gets the path for the logs package of beego. */ - string logsPackagePath() { - result = package(v1modulePath(), "logs") - or - result = package(v2modulePath(), "core/logs") - } - - /** Gets the path for the utils package of beego. */ - string utilsPackagePath() { - result = package(v1modulePath(), "utils") - or - result = package(v2modulePath(), "core/utils") - } - - /** - * `BeegoInput` sources of untrusted data. - */ - private class BeegoInputSource extends UntrustedFlowSource::Range { - string methodName; - FunctionOutput output; - - BeegoInputSource() { - exists(DataFlow::MethodCallNode c | this = output.getExitNode(c) | - c.getTarget().hasQualifiedName(contextPackagePath(), "BeegoInput", methodName) - ) and - ( - methodName = "Bind" and - output.isParameter(0) - or - methodName in [ - "Cookie", "Data", "GetData", "Header", "Param", "Params", "Query", "Refer", "Referer", - "URI", "URL", "UserAgent" - ] and - output.isResult(0) - ) - } - - predicate isSafeUrlSource() { methodName in ["URI", "URL"] } - } - - /** `BeegoInput` sources that are safe to use for redirection. */ - private class BeegoInputSafeUrlSource extends SafeUrlFlow::Source { - BeegoInputSafeUrlSource() { this.(BeegoInputSource).isSafeUrlSource() } - } - - /** - * `beego.Controller` sources of untrusted data. - */ - private class BeegoControllerSource extends UntrustedFlowSource::Range { - string methodName; - FunctionOutput output; - - BeegoControllerSource() { - exists(DataFlow::MethodCallNode c | - c.getTarget().hasQualifiedName(packagePath(), "Controller", methodName) - | - this = output.getExitNode(c) - ) and - ( - methodName = "ParseForm" and - output.isParameter(0) - or - methodName in ["GetFile", "GetFiles", "GetString", "GetStrings", "Input"] and - output.isResult(0) - or - methodName = "GetFile" and - output.isResult(1) - ) - } - } - - /** - * `beego/context.Context` sources of untrusted data. - */ - private class BeegoContextSource extends UntrustedFlowSource::Range { - BeegoContextSource() { - exists(Method m | m.hasQualifiedName(contextPackagePath(), "Context", "GetCookie") | - this = m.getACall().getResult() - ) - } - } - - private class BeegoOutputInstance extends HTTP::ResponseWriter::Range { - SsaWithFields v; - - BeegoOutputInstance() { - this = v.getBaseVariable().getSourceVariable() and - v.getType().(PointerType).getBaseType().hasQualifiedName(contextPackagePath(), "BeegoOutput") - } - - override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() } - - /** Gets a header object that corresponds to this HTTP response. */ - DataFlow::MethodCallNode getAHeaderObject() { - result.getTarget().getName() = ["ContentType", "Header"] and - this.getANode() = result.getReceiver() - } - } - - private class BeegoHeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::MethodCallNode { - string methodName; - - BeegoHeaderWrite() { - this.getTarget().hasQualifiedName(contextPackagePath(), "BeegoOutput", methodName) and - methodName in ["ContentType", "Header"] - } - - override DataFlow::Node getName() { methodName = "Header" and result = this.getArgument(0) } - - override string getHeaderName() { - result = HTTP::HeaderWrite::Range.super.getHeaderName() - or - methodName = "ContentType" and result = "content-type" - } - - override DataFlow::Node getValue() { - if methodName = "ContentType" - then result = this.getArgument(0) - else result = this.getArgument(1) - } - - override HTTP::ResponseWriter getResponseWriter() { - result.(BeegoOutputInstance).getAHeaderObject() = this - } - } - - private class BeegoResponseBody extends HTTP::ResponseBody::Range { - DataFlow::MethodCallNode call; - string methodName; - - BeegoResponseBody() { - exists(Method m | m.hasQualifiedName(contextPackagePath(), "BeegoOutput", methodName) | - call = m.getACall() and - this = call.getArgument(0) - ) and - methodName in ["Body", "JSON", "JSONP", "ServeFormatted", "XML", "YAML"] - } - - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = call.getReceiver() } - - override string getAContentType() { - // Super-method provides content-types for `Body`, which requires us to search - // for `ContentType` and `Header` calls against the same `BeegoOutput` instance - result = super.getAContentType() - or - // Specifically describe methods that set the content-type and body in one operation: - result = "application/json" and methodName = "JSON" - or - result = "application/javascript" and methodName = "JSONP" - or - // Actually ServeFormatted can serve JSON, XML or YAML depending on the incoming - // `Accept` header, but the important bit is this method cannot serve text/html. - result = "application/json" and methodName = "ServeFormatted" - or - result = "text/xml" and methodName = "XML" - or - result = "application/x-yaml" and methodName = "YAML" - } - } - - private class ControllerResponseBody extends HTTP::ResponseBody::Range { - string name; - - ControllerResponseBody() { - exists(Method m | m.hasQualifiedName(packagePath(), "Controller", name) | - name = "CustomAbort" and this = m.getACall().getArgument(1) - or - name = "SetData" and this = m.getACall().getArgument(0) - ) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - override string getAContentType() { - // Actually SetData can serve JSON, XML or YAML depending on the incoming - // `Accept` header, but the important bit is this method cannot serve text/html. - result = "application/json" and name = "SetData" - // CustomAbort doesn't specify a content type, so we assume anything could happen. - } - } - - private class ContextResponseBody extends HTTP::ResponseBody::Range { - string name; - - ContextResponseBody() { - exists(Method m | m.hasQualifiedName(contextPackagePath(), "Context", name) | - name = "Abort" and this = m.getACall().getArgument(1) - or - name = "WriteString" and this = m.getACall().getArgument(0) - ) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - // Neither method is likely to be used with well-typed data such as JSON output, - // because there are better methods to do this. Assume the Content-Type could - // be anything. - override string getAContentType() { none() } - } - - private string getALogFunctionName() { - result = - [ - "Alert", "Critical", "Debug", "Emergency", "Error", "Info", "Informational", "Notice", - "Trace", "Warn", "Warning" - ] - } - - private class ToplevelBeegoLoggers extends LoggerCall::Range, DataFlow::CallNode { - ToplevelBeegoLoggers() { - this.getTarget().hasQualifiedName([packagePath(), logsPackagePath()], getALogFunctionName()) - } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - - private class BeegoLoggerMethods extends LoggerCall::Range, DataFlow::MethodCallNode { - BeegoLoggerMethods() { - this.getTarget().hasQualifiedName(logsPackagePath(), "BeeLogger", getALogFunctionName()) - } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - - private class UtilLoggers extends LoggerCall::Range, DataFlow::CallNode { - UtilLoggers() { this.getTarget().hasQualifiedName(utilsPackagePath(), "Display") } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - - private class TopLevelTaintPropagators extends TaintTracking::FunctionModel { - string name; - - TopLevelTaintPropagators() { - this.hasQualifiedName(packagePath(), name) and - name in ["HTML2str", "Htmlquote", "Htmlunquote", "MapGet", "ParseForm", "Str2html", "Substr"] - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - name in ["HTML2str", "Htmlquote", "Htmlunquote", "MapGet", "Str2html", "Substr"] and - input.isParameter(0) and - output.isResult(0) - or - name = "ParseForm" and - input.isParameter(0) and - output.isParameter(1) - } - } - - private class ContextTaintPropagators extends TaintTracking::FunctionModel { - ContextTaintPropagators() { this.hasQualifiedName(contextPackagePath(), "WriteBody") } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isParameter(2) and output.isParameter(1) - } - } - - private class HtmlQuoteSanitizer extends SharedXss::Sanitizer { - HtmlQuoteSanitizer() { - exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName(packagePath(), "Htmlquote") | - this = c.getArgument(0) - ) - } - } - - private class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode { - FsOperations() { - this.getTarget().hasQualifiedName(packagePath(), "Walk") - or - exists(Method m | this = m.getACall() | - m.hasQualifiedName(packagePath(), "FileSystem", "Open") or - m.hasQualifiedName(packagePath(), "Controller", "SaveToFile") - ) - } - - override DataFlow::Node getAPathArgument() { - this.getTarget().getName() = ["Walk", "SaveToFile"] and result = this.getArgument(1) - or - this.getTarget().getName() = "Open" and result = this.getArgument(0) - } - } - - private class RedirectMethods extends HTTP::Redirect::Range, DataFlow::CallNode { - string package; - string className; - - RedirectMethods() { - ( - package = packagePath() and className = "Controller" - or - package = contextPackagePath() and className = "Context" - ) and - this = any(Method m | m.hasQualifiedName(package, className, "Redirect")).getACall() - } - - override DataFlow::Node getUrl() { - className = "Controller" and result = this.getArgument(0) - or - className = "Context" and result = this.getArgument(1) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - private class UtilsTaintPropagators extends TaintTracking::FunctionModel { - string name; - - UtilsTaintPropagators() { - this.hasQualifiedName(utilsPackagePath(), name) and - name in [ - "GetDisplayString", "SliceChunk", "SliceDiff", "SliceFilter", "SliceIntersect", - "SliceMerge", "SlicePad", "SliceRand", "SliceReduce", "SliceShuffle", "SliceUnique" - ] - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - name in [ - "GetDisplayString", "SliceIntersect", "SliceMerge", "SlicePad", "SliceRand", - "SliceShuffle", "SliceUnique" - ] and - input.isParameter(_) and - output.isResult(0) - or - name in ["SliceChunk", "SliceDiff", "SliceFilter", "SliceReduce"] and - input.isParameter(0) and - output.isResult(0) - } - } - - private class BeeMapModels extends TaintTracking::FunctionModel, Method { - string name; - - BeeMapModels() { - this.hasQualifiedName(utilsPackagePath(), "BeeMap", name) and - name in ["Get", "Set", "Items"] - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - name = "Set" and input.isParameter(1) and output.isReceiver() - or - name in ["Get", "Items"] and input.isReceiver() and output.isResult(0) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/BeegoOrm.qll b/ql/lib/semmle/go/frameworks/BeegoOrm.qll deleted file mode 100644 index f83556c30..000000000 --- a/ql/lib/semmle/go/frameworks/BeegoOrm.qll +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources, sinks and taint propagators - * from the `github.com/astaxie/beego/orm` subpackage. - */ - -import go -private import semmle.go.security.StoredXssCustomizations - -/** - * Provides classes for working with untrusted flow sources, sinks and taint propagators - * from the [Beego ORM](https://github.com/astaxie/beego/orm) subpackage. - */ -module BeegoOrm { - /** Gets the package name `github.com/astaxie/beego/orm`. */ - string packagePath() { result = package("github.com/astaxie/beego", "orm") } - - private class DbSink extends SQL::QueryString::Range { - DbSink() { - exists(Method m, string methodName, int argNum | - m.hasQualifiedName(packagePath(), "DB", methodName) and - methodName in [ - "Exec", "ExecContext", "Prepare", "PrepareContext", "Query", "QueryContext", "QueryRow", - "QueryRowContext" - ] and - if methodName.matches("%Context") then argNum = 1 else argNum = 0 - | - this = m.getACall().getArgument(argNum) - ) - } - } - - private class QueryBuilderSink extends SQL::QueryString::Range { - // Note this class doesn't do any escaping, unlike the true ORM part of the package - QueryBuilderSink() { - exists(Method impl | impl.implements(packagePath(), "QueryBuilder", _) | - this = impl.getACall().getAnArgument() - ) and - this.getType().getUnderlyingType() instanceof StringType - } - } - - private class OrmerRawSink extends SQL::QueryString::Range { - OrmerRawSink() { - exists(Method impl | impl.implements(packagePath(), "Ormer", "Raw") | - this = impl.getACall().getArgument(0) - ) - } - } - - private class QuerySeterFilterRawSink extends SQL::QueryString::Range { - QuerySeterFilterRawSink() { - exists(Method impl | impl.implements(packagePath(), "QuerySeter", "FilterRaw") | - this = impl.getACall().getArgument(1) - ) - } - } - - private class ConditionRawSink extends SQL::QueryString::Range { - ConditionRawSink() { - exists(Method impl | impl.implements(packagePath(), "Condition", "Raw") | - this = impl.getACall().getArgument(1) - ) - } - } - - private class OrmerSource extends StoredXss::Source { - OrmerSource() { - exists(Method impl | - impl.implements(packagePath(), "Ormer", ["Read", "ReadForUpdate", "ReadOrCreate"]) - | - this = FunctionOutput::parameter(0).getExitNode(impl.getACall()) - ) - } - } - - private class StringFieldSource extends StoredXss::Source { - StringFieldSource() { - exists(Method m | - m.hasQualifiedName(packagePath(), ["JSONField", "JsonbField", "TextField"], - ["RawValue", "String", "Value"]) - | - this = m.getACall().getResult() - ) - } - } - - private class SeterSource extends StoredXss::Source { - SeterSource() { - exists(Method impl | - // All and One are exclusive to QuerySeter, QueryRow[s] are exclusive to RawSeter, the rest are common. - impl.implements(packagePath(), ["QuerySeter", "RawSeter"], - [ - "All", "One", "Values", "ValuesList", "ValuesFlat", "RowsToMap", "RowsToStruct", - "QueryRow", "QueryRows" - ]) - | - this = FunctionOutput::parameter(0).getExitNode(impl.getACall()) - ) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Chi.qll b/ql/lib/semmle/go/frameworks/Chi.qll deleted file mode 100644 index 89f1a41d3..000000000 --- a/ql/lib/semmle/go/frameworks/Chi.qll +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources from the `github.com/go-chi/chi` package. - */ - -import go - -private module Chi { - /** Gets the package name `github.com/go-chi/chi`. */ - string packagePath() { result = package("github.com/go-chi/chi", "") } - - /** - * Functions that extract URL parameters, considered as a source of untrusted flow. - */ - private class UserControlledFunction extends UntrustedFlowSource::Range, DataFlow::CallNode { - UserControlledFunction() { - this.getTarget().hasQualifiedName(packagePath(), ["URLParam", "URLParamFromCtx"]) - } - } - - /** - * Methods that extract URL parameters, considered as a source of untrusted flow. - */ - private class UserControlledRequestMethod extends UntrustedFlowSource::Range, - DataFlow::MethodCallNode { - UserControlledRequestMethod() { - this.getTarget().hasQualifiedName(packagePath(), "Context", "URLParam") - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Couchbase.qll b/ql/lib/semmle/go/frameworks/Couchbase.qll deleted file mode 100644 index 983c445d7..000000000 --- a/ql/lib/semmle/go/frameworks/Couchbase.qll +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Provides models of commonly used functions in the official Couchbase Go SDK library. - */ - -import go - -/** - * Provides models of commonly used functions in the official Couchbase Go SDK library. - */ -module Couchbase { - /** - * Gets a package path for the official Couchbase Go SDK library. - * - * Note that v1 and v2 have different APIs, but the names are disjoint so there is no need to - * distinguish between them. - */ - string packagePath() { - result = - package([ - "gopkg.in/couchbase/gocb", "github.com/couchbase/gocb", "github.com/couchbaselabs/gocb" - ], "") - } - - /** - * Models of methods on `gocb/AnalyticsQuery` and `gocb/N1qlQuery` which which support a fluent - * interface by returning the receiver. They are not inherently relevant to taint. - */ - private class QueryMethodV1 extends TaintTracking::FunctionModel, Method { - QueryMethodV1() { - exists(string queryTypeName, string methodName | - queryTypeName = "AnalyticsQuery" and - methodName in [ - "ContextId", "Deferred", "Pretty", "Priority", "RawParam", "ServerSideTimeout" - ] - or - queryTypeName = "N1qlQuery" and - methodName in [ - "AdHoc", "Consistency", "ConsistentWith", "Custom", "PipelineBatch", "PipelineCap", - "Profile", "ReadOnly", "ScanCap", "Timeout" - ] - | - this.hasQualifiedName(packagePath(), queryTypeName, methodName) - ) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class QueryFromN1qlStatementV1 extends TaintTracking::FunctionModel { - QueryFromN1qlStatementV1() { - this.hasQualifiedName(packagePath(), ["NewAnalyticsQuery", "NewN1qlQuery"]) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isParameter(0) and outp.isResult() - } - } - - /** - * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of - * the official Couchbase Go library, gocb. - */ - private class CouchbaseV1Query extends NoSQL::Query::Range { - CouchbaseV1Query() { - // func (b *Bucket) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error) - // func (b *Bucket) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error) - // func (c *Cluster) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error) - // func (c *Cluster) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error) - exists(Method meth, string structName, string methodName | - structName in ["Bucket", "Cluster"] and - methodName in ["ExecuteN1qlQuery", "ExecuteAnalyticsQuery"] and - meth.hasQualifiedName(packagePath(), structName, methodName) and - this = meth.getACall().getArgument(0) - ) - } - } - - /** - * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of - * the official Couchbase Go library, gocb. - */ - private class CouchbaseV2Query extends NoSQL::Query::Range { - CouchbaseV2Query() { - // func (c *Cluster) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error) - // func (c *Cluster) Query(statement string, opts *QueryOptions) (*QueryResult, error) - // func (s *Scope) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error) - // func (s *Scope) Query(statement string, opts *QueryOptions) (*QueryResult, error) - exists(Method meth, string structName, string methodName | - structName in ["Cluster", "Scope"] and - methodName in ["AnalyticsQuery", "Query"] and - meth.hasQualifiedName(packagePath(), structName, methodName) and - this = meth.getACall().getArgument(0) - ) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Echo.qll b/ql/lib/semmle/go/frameworks/Echo.qll deleted file mode 100644 index df58b492b..000000000 --- a/ql/lib/semmle/go/frameworks/Echo.qll +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources, taint propagators, and HTTP sinks - * from the `github.com/labstack/echo` package. - */ - -import go - -private module Echo { - /** Gets the package name `github.com/labstack/echo`. */ - private string packagePath() { result = package("github.com/labstack/echo", "") } - - /** - * Data from a `Context` interface method, considered as a source of untrusted flow. - */ - private class EchoContextSource extends UntrustedFlowSource::Range { - EchoContextSource() { - exists(DataFlow::MethodCallNode call, string methodName | - methodName = - [ - "Param", "ParamValues", "QueryParam", "QueryParams", "QueryString", "FormValue", - "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies" - ] and - call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and - this = call.getResult(0) - ) - } - } - - /** - * Data from a `Context` interface method that is not generally exploitable for open-redirect attacks. - */ - private class EchoContextRedirectUnexploitableSource extends HTTP::Redirect::UnexploitableSource { - EchoContextRedirectUnexploitableSource() { - exists(DataFlow::MethodCallNode call, string methodName | - methodName = ["FormValue", "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and - call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and - this = call.getResult(0) - ) - } - } - - /** - * Models of `Context.Get/Set`. `Context` behaves like a map, with corresponding taint propagation. - */ - private class ContextMapModels extends TaintTracking::FunctionModel, Method { - string methodName; - FunctionInput input; - FunctionOutput output; - - ContextMapModels() { - ( - methodName = "Get" and input.isReceiver() and output.isResult() - or - methodName = "Set" and input.isParameter(1) and output.isReceiver() - ) and - this.hasQualifiedName(packagePath(), "Context", methodName) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp = input and outp = output - } - } - - /** - * A call to a method on `Context` struct that unmarshals data into a target. - */ - private class EchoContextBinder extends UntrustedFlowSource::Range { - EchoContextBinder() { - exists(DataFlow::MethodCallNode call | - call.getTarget().hasQualifiedName(packagePath(), "Context", "Bind") - | - this = FunctionOutput::parameter(0).getExitNode(call) - ) - } - } - - /** - * `echo.Context` methods which set the content-type to `text/html` and write a result in one operation. - */ - private class EchoHtmlOutputs extends HTTP::ResponseBody::Range { - EchoHtmlOutputs() { - exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["HTML", "HTMLBlob"]) | - this = m.getACall().getArgument(1) - ) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - override string getAContentType() { result = "text/html" } - } - - /** - * `echo.Context` methods which take a content-type as a parameter. - */ - private class EchoParameterizedOutputs extends HTTP::ResponseBody::Range { - DataFlow::CallNode callNode; - - EchoParameterizedOutputs() { - exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["Blob", "Stream"]) | - callNode = m.getACall() and this = callNode.getArgument(2) - ) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) } - } - - /** - * The `echo.Context.Redirect` method. - */ - private class EchoRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode { - EchoRedirectMethod() { - exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") | - this = m.getACall() - ) - } - - override DataFlow::Node getUrl() { result = this.getArgument(1) } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } -} diff --git a/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll b/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll deleted file mode 100644 index 0d49431e7..000000000 --- a/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Provides classes for working with concepts relating to the [github.com/elazarl/goproxy](https://pkg.go.dev/github.com/elazarl/goproxy) package. - */ - -import go -private import semmle.go.StringOps - -/** - * Provides classes for working with concepts relating to the [github.com/elazarl/goproxy](https://pkg.go.dev/github.com/elazarl/goproxy) package. - */ -module ElazarlGoproxy { - /** Gets the package name. */ - string packagePath() { result = package("github.com/elazarl/goproxy", "") } - - private class NewResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode { - NewResponse() { this.getTarget().hasQualifiedName(packagePath(), "NewResponse") } - - override string getHeaderName() { this.definesHeader(result, _) } - - override string getHeaderValue() { this.definesHeader(_, result) } - - override DataFlow::Node getName() { none() } - - override DataFlow::Node getValue() { result = this.getArgument([1, 2]) } - - override predicate definesHeader(string header, string value) { - header = "status" and value = this.getArgument(2).getIntValue().toString() - or - header = "content-type" and value = this.getArgument(1).getStringValue() - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - /** A body argument to a `NewResponse` call. */ - private class NewResponseBody extends HTTP::ResponseBody::Range { - NewResponse call; - - NewResponseBody() { this = call.getArgument(3) } - - override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - private class TextResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode { - TextResponse() { this.getTarget().hasQualifiedName(packagePath(), "TextResponse") } - - override string getHeaderName() { this.definesHeader(result, _) } - - override string getHeaderValue() { this.definesHeader(_, result) } - - override DataFlow::Node getName() { none() } - - override DataFlow::Node getValue() { none() } - - override predicate definesHeader(string header, string value) { - header = "status" and value = "200" - or - header = "content-type" and value = "text/plain" - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - /** A body argument to a `TextResponse` call. */ - private class TextResponseBody extends HTTP::ResponseBody::Range, TextResponse { - TextResponse call; - - TextResponseBody() { this = call.getArgument(2) } - - override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - /** A handler attached to a goproxy proxy type. */ - private class ProxyHandler extends HTTP::RequestHandler::Range { - DataFlow::MethodCallNode handlerReg; - - ProxyHandler() { - handlerReg - .getTarget() - .hasQualifiedName(packagePath(), "ReqProxyConds", ["Do", "DoFunc", "HandleConnect"]) and - this = handlerReg.getArgument(0) - } - - override predicate guardedBy(DataFlow::Node check) { - // note OnResponse is not modeled, as that server responses are not currently considered untrusted input - exists(DataFlow::MethodCallNode onreqcall | - onreqcall.getTarget().hasQualifiedName(packagePath(), "ProxyHttpServer", "OnRequest") - | - handlerReg.getReceiver() = onreqcall.getASuccessor*() and - check = onreqcall.getArgument(0) - ) - } - } - - private class UserControlledRequestData extends UntrustedFlowSource::Range { - UserControlledRequestData() { - exists(DataFlow::FieldReadNode frn | this = frn | - // liberally consider ProxyCtx.UserData to be untrusted; it's a data field set by a request handler - frn.getField().hasQualifiedName(packagePath(), "ProxyCtx", "UserData") - ) - or - exists(DataFlow::MethodCallNode call | this = call | - call.getTarget().hasQualifiedName(packagePath(), "ProxyCtx", "Charset") - ) - } - } - - private class ProxyLogFunction extends StringOps::Formatting::Range, Method { - ProxyLogFunction() { this.hasQualifiedName(packagePath(), "ProxyCtx", ["Logf", "Warnf"]) } - - override int getFormatStringIndex() { result = 0 } - - override int getFirstFormattedParameterIndex() { result = 1 } - } - - private class ProxyLog extends LoggerCall::Range, DataFlow::MethodCallNode { - ProxyLog() { this.getTarget() instanceof ProxyLogFunction } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - - private class MethodModels extends TaintTracking::FunctionModel, Method { - FunctionInput inp; - FunctionOutput outp; - - MethodModels() { - // Methods: - // signature: func CertStorage.Fetch(hostname string, gen func() (*tls.Certificate, error)) (*tls.Certificate, error) - // - // `hostname` excluded because if the cert storage or generator function themselves have not - // been tainted, `hostname` would be unlikely to fetch user-controlled data - this.hasQualifiedName(packagePath(), "CertStorage", "Fetch") and - (inp.isReceiver() or inp.isParameter(1)) and - outp.isResult(0) - } - - override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) { i = inp and o = outp } - } -} diff --git a/ql/lib/semmle/go/frameworks/Email.qll b/ql/lib/semmle/go/frameworks/Email.qll deleted file mode 100644 index 049af5f87..000000000 --- a/ql/lib/semmle/go/frameworks/Email.qll +++ /dev/null @@ -1,114 +0,0 @@ -/** Provides classes for working with email-related APIs. */ - -import go - -/** - * A data-flow node that represents data written to an email, either as part - * of the headers or as part of the body. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `EmailData::Range` instead. - */ -class EmailData extends DataFlow::Node { - EmailData::Range self; - - EmailData() { this = self } -} - -/** Provides classes for working with data that is incorporated into an email. */ -module EmailData { - /** - * A data-flow node that represents data which is written to an email, either as part - * of the headers or as part of the body. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `EmailData` instead. - */ - abstract class Range extends DataFlow::Node { } - - /** A data-flow node that is written to an email using the net/smtp package. */ - private class SmtpData extends Range { - SmtpData() { - // func (c *Client) Data() (io.WriteCloser, error) - exists(Method data | - data.hasQualifiedName("net/smtp", "Client", "Data") and - this.(DataFlow::SsaNode).getInit() = data.getACall().getResult(0) - ) - or - // func SendMail(addr string, a Auth, from string, to []string, msg []byte) error - exists(Function sendMail | - sendMail.hasQualifiedName("net/smtp", "SendMail") and - this = sendMail.getACall().getArgument(4) - ) - } - } - - /** Gets the package name `github.com/sendgrid/sendgrid-go/helpers/mail`. */ - private string sendgridMail() { - result = package("github.com/sendgrid/sendgrid-go", "helpers/mail") - } - - private class NewContent extends TaintTracking::FunctionModel { - NewContent() { - // func NewContent(contentType string, value string) *Content - this.hasQualifiedName(sendgridMail(), "NewContent") - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isParameter(1) and output.isResult() - } - } - - /** A data-flow node that is written to an email using the sendgrid/sendgrid-go package. */ - private class SendGridEmail extends Range { - SendGridEmail() { - // func NewSingleEmail(from *Email, subject string, to *Email, plainTextContent string, htmlContent string) *SGMailV3 - exists(Function newSingleEmail | - newSingleEmail.hasQualifiedName(sendgridMail(), "NewSingleEmail") and - this = newSingleEmail.getACall().getArgument([1, 3, 4]) - ) - or - // func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3 - exists(Function newv3MailInit | - newv3MailInit.hasQualifiedName(sendgridMail(), "NewV3MailInit") and - this = newv3MailInit.getACall().getArgument(any(int i | i = 1 or i >= 3)) - ) - or - // func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3 - exists(Method addContent | - addContent.hasQualifiedName(sendgridMail(), "SGMailV3", "AddContent") and - this = addContent.getACall().getAnArgument() - ) - } - } -} - -/** - * A taint model of the `Writer.CreatePart` method from `mime/multipart`. - * - * If tainted data is written to the multipart section created by this method, the underlying writer - * should be considered tainted as well. - */ -private class MultipartWriterCreatePartModel extends TaintTracking::FunctionModel, Method { - MultipartWriterCreatePartModel() { - this.hasQualifiedName("mime/multipart", "Writer", "CreatePart") - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isResult(0) and output.isReceiver() - } -} - -/** - * A taint model of the `NewWriter` function from `mime/multipart`. - * - * If tainted data is written to the writer created by this function, the underlying writer - * should be considered tainted as well. - */ -private class MultipartNewWriterModel extends TaintTracking::FunctionModel { - MultipartNewWriterModel() { this.hasQualifiedName("mime/multipart", "NewWriter") } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isResult() and output.isParameter(0) - } -} diff --git a/ql/lib/semmle/go/frameworks/Encoding.qll b/ql/lib/semmle/go/frameworks/Encoding.qll deleted file mode 100644 index 34af4ce6e..000000000 --- a/ql/lib/semmle/go/frameworks/Encoding.qll +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Provides classes modeling taint propagation through marshalling and encoding functions. - */ - -import go - -/** Gets the package name `github.com/json-iterator/go`. */ -private string packagePath() { result = package("github.com/json-iterator/go", "") } - -/** A model of json-iterator's `Unmarshal` function, propagating taint from the JSON input to the decoded object. */ -private class JsonIteratorUnmarshalFunction extends TaintTracking::FunctionModel, - UnmarshalingFunction::Range { - JsonIteratorUnmarshalFunction() { - this.hasQualifiedName(packagePath(), ["Unmarshal", "UnmarshalFromString"]) - or - this.(Method).implements(packagePath(), "API", ["Unmarshal", "UnmarshalFromString"]) - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } - - override string getFormat() { result = "JSON" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } -} diff --git a/ql/lib/semmle/go/frameworks/EvanphxJsonPatch.qll b/ql/lib/semmle/go/frameworks/EvanphxJsonPatch.qll deleted file mode 100644 index 6408b3d1d..000000000 --- a/ql/lib/semmle/go/frameworks/EvanphxJsonPatch.qll +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Provides classes modeling `github.com/evanphx/json-patch`. - */ - -import go - -private module EvanphxJsonPatch { - /** Gets the package name `github.com/evanphx/json-patch`. */ - private string packagePath() { result = package("github.com/evanphx/json-patch", "") } - - private class MergeMergePatches extends TaintTracking::FunctionModel { - MergeMergePatches() { this.hasQualifiedName(packagePath(), "MergeMergePatches") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - (inp.isParameter(0) or inp.isParameter(1)) and - outp.isResult(0) - } - } - - private class MergePatch extends TaintTracking::FunctionModel { - MergePatch() { this.hasQualifiedName(packagePath(), "MergePatch") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - (inp.isParameter(0) or inp.isParameter(1)) and - outp.isResult(0) - } - } - - private class CreateMergePatch extends TaintTracking::FunctionModel { - CreateMergePatch() { this.hasQualifiedName(packagePath(), "CreateMergePatch") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - (inp.isParameter(0) or inp.isParameter(1)) and - outp.isResult(0) - } - } - - private class DecodePatch extends TaintTracking::FunctionModel { - DecodePatch() { this.hasQualifiedName(packagePath(), "DecodePatch") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and - outp.isResult(0) - } - } - - private class Apply extends TaintTracking::FunctionModel, Method { - Apply() { - exists(string fn | - fn in ["Apply", "ApplyWithOptions", "ApplyIndent", "ApplyIndentWithOptions"] - | - this.hasQualifiedName(packagePath(), "Patch", fn) - ) - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - (inp.isParameter(0) or inp.isReceiver()) and - outp.isResult(0) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Gin.qll b/ql/lib/semmle/go/frameworks/Gin.qll deleted file mode 100644 index 87212868a..000000000 --- a/ql/lib/semmle/go/frameworks/Gin.qll +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources from the `github.com/gin-gonic/gin` package. - */ - -import go - -private module Gin { - /** Gets the package name `github.com/gin-gonic/gin`. */ - string packagePath() { result = package("github.com/gin-gonic/gin", "") } - - /** - * Data from a `Context` struct, considered as a source of untrusted flow. - */ - private class GithubComGinGonicGinContextSource extends UntrustedFlowSource::Range { - GithubComGinGonicGinContextSource() { - // Method calls: - exists(DataFlow::MethodCallNode call, string methodName | - call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and - methodName in [ - "FullPath", "GetHeader", "QueryArray", "Query", "PostFormArray", "PostForm", "Param", - "GetStringSlice", "GetString", "GetRawData", "ClientIP", "ContentType", "Cookie", - "GetQueryArray", "GetQuery", "GetPostFormArray", "GetPostForm", "DefaultPostForm", - "DefaultQuery", "GetPostFormMap", "GetQueryMap", "GetStringMap", "GetStringMapString", - "GetStringMapStringSlice", "PostFormMap", "QueryMap" - ] - | - this = call.getResult(0) - ) - or - // Field reads: - exists(DataFlow::Field fld | - fld.hasQualifiedName(packagePath(), "Context", ["Accepted", "Params"]) and - this = fld.getARead() - ) - } - } - - private class ParamsGet extends TaintTracking::FunctionModel, Method { - ParamsGet() { this.hasQualifiedName(packagePath(), "Params", "Get") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult(0) - } - } - - private class ParamsByName extends TaintTracking::FunctionModel, Method { - ParamsByName() { this.hasQualifiedName(packagePath(), "Params", "ByName") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - /** - * A call to a method on `Context` struct that unmarshals data into a target. - */ - private class GithubComGinGonicGinContextBindSource extends UntrustedFlowSource::Range { - GithubComGinGonicGinContextBindSource() { - exists(DataFlow::MethodCallNode call, string methodName | - call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and - methodName in [ - "BindJSON", "BindYAML", "BindXML", "BindUri", "BindQuery", "BindWith", "BindHeader", - "MustBindWith", "Bind", "ShouldBind", "ShouldBindBodyWith", "ShouldBindJSON", - "ShouldBindQuery", "ShouldBindUri", "ShouldBindHeader", "ShouldBindWith", - "ShouldBindXML", "ShouldBindYAML" - ] - | - this = FunctionOutput::parameter(0).getExitNode(call) - ) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Glog.qll b/ql/lib/semmle/go/frameworks/Glog.qll deleted file mode 100644 index 70b117a71..000000000 --- a/ql/lib/semmle/go/frameworks/Glog.qll +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Provides models of commonly used functions in the `github.com/golang/glog` and `k8s.io/klog` - * packages. - */ - -import go -private import semmle.go.StringOps - -/** - * Provides models of commonly used functions in the `github.com/golang/glog` packages and its - * forks. - */ -module Glog { - private class GlogFunction extends Function { - int firstPrintedArg; - - GlogFunction() { - exists(string pkg, string fn, string level | - pkg = package(["github.com/golang/glog", "gopkg.in/glog", "k8s.io/klog"], "") and - level = ["Error", "Exit", "Fatal", "Info", "Warning"] and - ( - fn = level + ["", "f", "ln"] and firstPrintedArg = 0 - or - fn = level + "Depth" and firstPrintedArg = 1 - ) - | - this.hasQualifiedName(pkg, fn) - or - this.(Method).hasQualifiedName(pkg, "Verbose", fn) - ) - } - - /** - * Gets the index of the first argument that may be output, including a format string if one is present. - */ - int getFirstPrintedArg() { result = firstPrintedArg } - } - - private class StringFormatter extends StringOps::Formatting::Range instanceof GlogFunction { - StringFormatter() { this.getName().matches("%f") } - - override int getFormatStringIndex() { result = super.getFirstPrintedArg() } - - override int getFirstFormattedParameterIndex() { result = super.getFirstPrintedArg() + 1 } - } - - private class GlogCall extends LoggerCall::Range, DataFlow::CallNode { - GlogFunction callee; - - GlogCall() { this = callee.getACall() } - - override DataFlow::Node getAMessageComponent() { - result = this.getArgument(any(int i | i >= callee.getFirstPrintedArg())) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/GoKit.qll b/ql/lib/semmle/go/frameworks/GoKit.qll deleted file mode 100644 index c50185899..000000000 --- a/ql/lib/semmle/go/frameworks/GoKit.qll +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Provides classes for working with concepts relating to the [github.com/go-kit/kit](https://pkg.go.dev/github.com/go-kit/kit) package. - * - * Note that these models are not included by default; to include them, add `import semmle.go.frameworks.GoKit` to your query or to - * `Customizations.qll`. - */ - -import go - -/** - * Provides classes for working with concepts relating to the [github.com/go-kit/kit](https://pkg.go.dev/github.com/go-kit/kit) package. - */ -module GoKit { - /** Gets the package name. */ - string packagePath() { result = package("github.com/go-kit/kit", "") } - - /** - * Provides classes for working with concepts relating to the `endpoint` package of the - * [github.com/go-kit/kit](https://pkg.go.dev/github.com/go-kit/kit) package. - */ - module Endpoint { - /** Gets the package name. */ - string endpointPackagePath() { result = package("github.com/go-kit/kit", "endpoint") } - - // gets a function that returns an endpoint - private DataFlow::Node getAnEndpointFactoryResult() { - exists(Function mkFn, FunctionOutput res | - mkFn.getResultType(0).hasQualifiedName(endpointPackagePath(), "Endpoint") and - result = res.getEntryNode(mkFn.getFuncDecl()).getAPredecessor*() - ) - } - - private FuncDef getAnEndpointFunction() { - exists(Function endpointFn | endpointFn.getFuncDecl() = result | - endpointFn.getARead() = getAnEndpointFactoryResult() - ) - or - DataFlow::exprNode(result.(FuncLit)) = getAnEndpointFactoryResult() - } - - private class EndpointRequest extends UntrustedFlowSource::Range { - EndpointRequest() { this = DataFlow::parameterNode(getAnEndpointFunction().getParameter(1)) } - } - } -} diff --git a/ql/lib/semmle/go/frameworks/GoRestfulHttp.qll b/ql/lib/semmle/go/frameworks/GoRestfulHttp.qll deleted file mode 100644 index ddaf4dde5..000000000 --- a/ql/lib/semmle/go/frameworks/GoRestfulHttp.qll +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Provides models of the [go-restful library](https://github.com/emicklei/go-restful). - */ - -import go - -/** - * Provides models of the [go-restful library](https://github.com/emicklei/go-restful). - */ -private module GoRestfulHttp { - /** Gets the package name `github.com/emicklei/go-restful`. */ - string packagePath() { result = package("github.com/emicklei/go-restful", "") } - - /** - * A model for methods defined on go-restful's `Request` object that may return user-controlled data. - */ - private class GoRestfulSourceMethod extends Method { - GoRestfulSourceMethod() { - this.hasQualifiedName(packagePath(), "Request", - [ - "QueryParameters", "QueryParameter", "BodyParameter", "HeaderParameter", "PathParameter", - "PathParameters" - ]) - } - } - - /** - * A model of go-restful's `Request` object as a source of user-controlled data. - */ - private class GoRestfulSource extends UntrustedFlowSource::Range { - GoRestfulSource() { this = any(GoRestfulSourceMethod g).getACall() } - } - - /** - * A model of go-restful's `Request.ReadEntity` method as a source of user-controlled data. - */ - private class GoRestfulReadEntitySource extends UntrustedFlowSource::Range { - GoRestfulReadEntitySource() { - exists(DataFlow::MethodCallNode call | - call.getTarget().hasQualifiedName(packagePath(), "Request", "ReadEntity") - | - this = FunctionOutput::parameter(0).getExitNode(call) - ) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/K8sIoApiCoreV1.qll b/ql/lib/semmle/go/frameworks/K8sIoApiCoreV1.qll deleted file mode 100644 index 9918274f1..000000000 --- a/ql/lib/semmle/go/frameworks/K8sIoApiCoreV1.qll +++ /dev/null @@ -1,61 +0,0 @@ -/** Provides models of commonly used functions in the `k8s.io/api/core/v1` package. */ - -import go - -/** - * Provides models of commonly used functions in the `k8s.io/api/core/v1` package. - */ -module K8sIoApiCoreV1 { - /** Gets the package name `k8s.io/api/core/v1`. */ - string packagePath() { result = package("k8s.io/api", "core/v1") } - - private class SecretDeepCopy extends TaintTracking::FunctionModel, Method { - string methodName; - FunctionOutput output; - - SecretDeepCopy() { - ( - methodName in ["DeepCopy", "DeepCopyObject"] and output.isResult() - or - methodName = "DeepCopyInto" and output.isParameter(0) - ) and - this.hasQualifiedName(packagePath(), ["Secret", "SecretList"], methodName) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp = outp - } - } - - private class SecretMarshal extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - SecretMarshal() { this.hasQualifiedName(packagePath(), ["Secret", "SecretList"], "Marshal") } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isResult(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class SecretUnmarshal extends TaintTracking::FunctionModel, Method, - UnmarshalingFunction::Range { - SecretUnmarshal() { - this.hasQualifiedName(packagePath(), ["Secret", "SecretList"], "Unmarshal") - } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } -} diff --git a/ql/lib/semmle/go/frameworks/K8sIoApimachineryPkgRuntime.qll b/ql/lib/semmle/go/frameworks/K8sIoApimachineryPkgRuntime.qll deleted file mode 100644 index 3ce4df3dc..000000000 --- a/ql/lib/semmle/go/frameworks/K8sIoApimachineryPkgRuntime.qll +++ /dev/null @@ -1,424 +0,0 @@ -/** Provides models of commonly used functions in the `k8s.io/apimachinery/pkg/runtime` package. */ - -import go - -/** - * Provides models of commonly used functions in the `k8s.io/apimachinery/pkg/runtime` package. - */ -module K8sIoApimachineryPkgRuntime { - /** Gets the package name `k8s.io/apimachinery/pkg/runtime`. */ - string packagePath() { result = package("k8s.io/apimachinery", "pkg/runtime") } - - private class ConvertTypeToType extends TaintTracking::FunctionModel { - ConvertTypeToType() { - this.hasQualifiedName(packagePath(), - [ - "Convert_Slice_string_To_Pointer_int64", "Convert_Slice_string_To_int", - "Convert_Slice_string_To_int64", "Convert_Slice_string_To_string", - "Convert_runtime_Object_To_runtime_RawExtension", - "Convert_runtime_RawExtension_To_runtime_Object", "Convert_string_To_Pointer_int64", - "Convert_string_To_int64" - ]) - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isParameter(1) - } - } - - private class DecodeInto extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { - DecodeInto() { this.hasQualifiedName(packagePath(), "DecodeInto") } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(1) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(2) } - - override string getFormat() { - // The format is not fixed. It depends on parameter 1 or 2. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class DeepCopyJSON extends TaintTracking::FunctionModel { - DeepCopyJSON() { this.hasQualifiedName(packagePath(), ["DeepCopyJSON", "DeepCopyJSONValue"]) } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isResult() - } - } - - private class Encode extends TaintTracking::FunctionModel, MarshalingFunction::Range { - Encode() { this.hasQualifiedName(packagePath(), ["Encode", "EncodeOrDie"]) } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(1) } - - override DataFlow::FunctionOutput getOutput() { result.isResult(0) } - - override string getFormat() { - // The format is not fixed. It depends on the receiver. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class ReadField extends TaintTracking::FunctionModel { - ReadField() { this.hasQualifiedName(packagePath(), ["Field", "FieldPtr"]) } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isParameter(2) - } - } - - private class SetField extends TaintTracking::FunctionModel { - SetField() { this.hasQualifiedName(packagePath(), "SetField") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isParameter(1) - } - } - - private class CacheableObjectCacheEncode extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - CacheableObjectCacheEncode() { - this.implements(packagePath(), "CacheableObject", "CacheEncode") - } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(2) } - - override string getFormat() { - // The format is not fixed. It depends on the receiver. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class CacheableObjectGetObject extends TaintTracking::FunctionModel, Method { - CacheableObjectGetObject() { this.implements(packagePath(), "CacheableObject", "GetObject") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class DecoderDecode extends TaintTracking::FunctionModel, Method, - UnmarshalingFunction::Range { - DecoderDecode() { - this.implements(packagePath(), "Decoder", "Decode") or - this.hasQualifiedName(packagePath(), "WithoutVersionDecoder", "Decode") - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(2) or result.isResult(0) } - - override string getFormat() { - // The format is not fixed. It depends on the receiver. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class EncoderEncode extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - EncoderEncode() { - this.implements(packagePath(), "Encoder", "Encode") or - this.hasQualifiedName(packagePath(), "WithVersionEncoder", "Encode") - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } - - override string getFormat() { - // The format is not fixed. It depends on the receiver. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class FramerNewFrameReader extends TaintTracking::FunctionModel, Method { - FramerNewFrameReader() { this.implements(packagePath(), "Framer", "NewFrameReader") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isResult() - } - } - - private class FramerNewFrameWriter extends TaintTracking::FunctionModel, Method { - FramerNewFrameWriter() { this.implements(packagePath(), "Framer", "NewFrameWriter") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isResult() - } - } - - private class ObjectDeepCopyObject extends TaintTracking::FunctionModel, Method { - ObjectDeepCopyObject() { this.implements(packagePath(), "Object", "DeepCopyObject") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class Decode extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { - Decode() { this.hasQualifiedName(packagePath(), "Decode") } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(1) } - - override DataFlow::FunctionOutput getOutput() { result.isResult(0) } - - override string getFormat() { - // The format is not fixed. It depends on the parameter 0. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class NewEncodable extends TaintTracking::FunctionModel { - NewEncodable() { this.hasQualifiedName(packagePath(), ["NewEncodable", "NewEncodableList"]) } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(1) and outp.isResult() - } - } - - private class UseOrCreateObject extends TaintTracking::FunctionModel { - UseOrCreateObject() { this.hasQualifiedName(packagePath(), "UseOrCreateObject") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(3) and outp.isResult(0) - } - } - - private class ObjectConvertorConvert extends TaintTracking::FunctionModel, Method { - ObjectConvertorConvert() { this.implements(packagePath(), "ObjectConvertor", "Convert") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isParameter(1) - } - } - - private class ObjectConvertorConvertToVersion extends TaintTracking::FunctionModel, Method { - ObjectConvertorConvertToVersion() { - this.implements(packagePath(), "ObjectConvertor", "ConvertToVersion") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isResult(0) - } - } - - private class ObjectVersionerConvertToVersion extends TaintTracking::FunctionModel, Method { - ObjectVersionerConvertToVersion() { - this.implements(packagePath(), "ObjectVersioner", "ConvertToVersion") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isResult(0) - } - } - - private class ParameterCodecDecodeParameters extends TaintTracking::FunctionModel, Method, - UnmarshalingFunction::Range { - ParameterCodecDecodeParameters() { - this.implements(packagePath(), "ParameterCodec", "DecodeParameters") - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(2) } - - override string getFormat() { - // The format is not fixed. It depends on parameter 1. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class ParameterCodecEncodeParameters extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - ParameterCodecEncodeParameters() { - this.implements(packagePath(), "ParameterCodec", "EncodeParameters") - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isResult(0) } - - override string getFormat() { - // The format is not fixed. It depends on parameter 1. - none() - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class ProtobufMarshallerMarshalTo extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - ProtobufMarshallerMarshalTo() { - this.implements(packagePath(), "ProtobufMarshaller", "MarshalTo") or - this.implements(packagePath(), "ProtobufReverseMarshaller", "MarshalToSizedBuffer") - } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class RawExtensionDeepCopy extends TaintTracking::FunctionModel, Method { - RawExtensionDeepCopy() { this.hasQualifiedName(packagePath(), "RawExtension", "DeepCopy") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class RawExtensionDeepCopyInto extends TaintTracking::FunctionModel, Method { - RawExtensionDeepCopyInto() { - this.hasQualifiedName(packagePath(), "RawExtension", "DeepCopyInto") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isParameter(0) - } - } - - private class RawExtensionMarshal extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - RawExtensionMarshal() { this.hasQualifiedName(packagePath(), "RawExtension", "Marshal") } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isResult(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class RawExtensionUnmarshal extends TaintTracking::FunctionModel, Method, - UnmarshalingFunction::Range { - RawExtensionUnmarshal() { this.hasQualifiedName(packagePath(), "RawExtension", "Unmarshal") } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class UnknownDeepCopy extends TaintTracking::FunctionModel, Method { - UnknownDeepCopy() { - this.hasQualifiedName(packagePath(), "Unknown", ["DeepCopy", "DeepCopyObject"]) - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class UnknownDeepCopyInto extends TaintTracking::FunctionModel, Method { - UnknownDeepCopyInto() { this.hasQualifiedName(packagePath(), "Unknown", "DeepCopyInto") } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isParameter(0) - } - } - - private class UnknownMarshal extends TaintTracking::FunctionModel, Method, - MarshalingFunction::Range { - string methodName; - - UnknownMarshal() { - methodName in ["Marshal", "NestedMarshalTo"] and - this.hasQualifiedName(packagePath(), "Unknown", methodName) - } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { - methodName = "Marshal" and result.isResult(0) - or - methodName = "NestedMarshalTo" and result.isParameter(0) - } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class UnknownUnmarshal extends TaintTracking::FunctionModel, Method, - UnmarshalingFunction::Range { - UnknownUnmarshal() { this.hasQualifiedName(packagePath(), "Unknown", "Unmarshal") } - - override DataFlow::FunctionInput getAnInput() { result.isReceiver() } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(0) } - - override string getFormat() { result = "protobuf" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - } - - private class UnstructuredUnstructuredContent extends TaintTracking::FunctionModel, Method { - UnstructuredUnstructuredContent() { - this.implements(packagePath(), "Unstructured", "UnstructuredContent") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class UnstructuredSetUnstructuredContent extends TaintTracking::FunctionModel, Method { - UnstructuredSetUnstructuredContent() { - this.implements(packagePath(), "Unstructured", "SetUnstructuredContent") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isParameter(0) and outp.isReceiver() - } - } -} diff --git a/ql/lib/semmle/go/frameworks/K8sIoClientGo.qll b/ql/lib/semmle/go/frameworks/K8sIoClientGo.qll deleted file mode 100644 index c087cc26f..000000000 --- a/ql/lib/semmle/go/frameworks/K8sIoClientGo.qll +++ /dev/null @@ -1,28 +0,0 @@ -/** Provides models of commonly used functions in the `k8s.io/client-go/kubernetes/typed/core/v1` package. */ - -import go - -/** - * Provides models of commonly used functions in the `k8s.io/client-go/kubernetes/typed/core/v1` - * package. - */ -module K8sIoClientGo { - /** Gets the package name `k8s.io/client-go/kubernetes/typed/core/v1`. */ - string packagePath() { result = package("k8s.io/client-go", "kubernetes/typed/core/v1") } - - /** - * A model of `SecretInterface` methods that are sources of secret data. - */ - private class SecretInterfaceSourceMethod extends Method { - SecretInterfaceSourceMethod() { - this.implements(packagePath(), "SecretInterface", ["Get", "List", "Patch"]) - } - } - - /** - * A model of `SecretInterface` as a source of secret data. - */ - class SecretInterfaceSource extends DataFlow::Node { - SecretInterfaceSource() { this = any(SecretInterfaceSourceMethod g).getACall().getResult(0) } - } -} diff --git a/ql/lib/semmle/go/frameworks/Logrus.qll b/ql/lib/semmle/go/frameworks/Logrus.qll deleted file mode 100644 index 9255b6767..000000000 --- a/ql/lib/semmle/go/frameworks/Logrus.qll +++ /dev/null @@ -1,53 +0,0 @@ -/** Provides models of commonly used functions in the `github.com/sirupsen/logrus` package. */ - -import go -private import semmle.go.StringOps - -/** Provides models of commonly used functions in the `github.com/sirupsen/logrus` package. */ -module Logrus { - /** Gets the package name `github.com/sirupsen/logrus`. */ - string packagePath() { - result = package(["github.com/sirupsen/logrus", "github.com/Sirupsen/logrus"], "") - } - - bindingset[result] - private string getALogResultName() { - result - .matches([ - "Debug%", "Error%", "Fatal%", "Info%", "Log%", "Panic%", "Print%", "Trace%", "Warn%" - ]) - } - - bindingset[result] - private string getAnEntryUpdatingMethodName() { - result.regexpMatch("With(Context|Error|Fields?|Time)") - } - - private class LogFunction extends Function { - LogFunction() { - exists(string name | name = getALogResultName() or name = getAnEntryUpdatingMethodName() | - this.hasQualifiedName(packagePath(), name) or - this.(Method).hasQualifiedName(packagePath(), ["Entry", "Logger"], name) - ) - } - } - - private class LogCall extends LoggerCall::Range, DataFlow::CallNode { - LogCall() { this = any(LogFunction f).getACall() } - - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } - } - - private class StringFormatters extends StringOps::Formatting::Range instanceof LogFunction { - int argOffset; - - StringFormatters() { - this.getName().matches("%f") and - if this.getName() = "Logf" then argOffset = 1 else argOffset = 0 - } - - override int getFormatStringIndex() { result = argOffset } - - override int getFirstFormattedParameterIndex() { result = argOffset + 1 } - } -} diff --git a/ql/lib/semmle/go/frameworks/Macaron.qll b/ql/lib/semmle/go/frameworks/Macaron.qll deleted file mode 100644 index a38b2b20d..000000000 --- a/ql/lib/semmle/go/frameworks/Macaron.qll +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Provides classes for working with concepts relating to the Macaron web framework - */ - -import go - -private module Macaron { - private class Context extends HTTP::ResponseWriter::Range { - SsaWithFields v; - - Context() { - this = v.getBaseVariable().getSourceVariable() and - exists(Method m | m.hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") | - v.getType().getMethod("Redirect") = m - ) - } - - override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() } - } - - private class RedirectCall extends HTTP::Redirect::Range, DataFlow::MethodCallNode { - RedirectCall() { - this.getTarget().hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") - } - - override DataFlow::Node getUrl() { result = this.getArgument(0) } - - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } - } -} diff --git a/ql/lib/semmle/go/frameworks/Mux.qll b/ql/lib/semmle/go/frameworks/Mux.qll deleted file mode 100644 index bca64c17c..000000000 --- a/ql/lib/semmle/go/frameworks/Mux.qll +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Provides classes for working with concepts in the Mux HTTP middleware library. - */ - -import go - -/** - * Provides classes for working with concepts in the Mux HTTP middleware library. - */ -module Mux { - /** An access to a Mux middleware variable. */ - class RequestVars extends DataFlow::UntrustedFlowSource::Range, DataFlow::CallNode { - RequestVars() { - this.getTarget().hasQualifiedName(package("github.com/gorilla/mux", ""), "Vars") - } - } -} diff --git a/ql/lib/semmle/go/frameworks/NoSQL.qll b/ql/lib/semmle/go/frameworks/NoSQL.qll deleted file mode 100644 index 9f9ca6090..000000000 --- a/ql/lib/semmle/go/frameworks/NoSQL.qll +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Provides classes for working with NoSQL-related concepts such as queries. - */ - -import go - -/** Provides classes for working with NoSQL-related APIs. */ -module NoSQL { - /** - * A data-flow node whose value is interpreted as (part of) a NoSQL query. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `NoSQL::Query::Range` instead. - */ - class Query extends DataFlow::Node { - Query::Range self; - - Query() { this = self } - } - - /** Provides classes for working with NoSQL queries. */ - module Query { - /** - * A data-flow node whose value is interpreted as (part of) a NoSQL query. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `NoSQL::Query` instead. - */ - abstract class Range extends DataFlow::Node { } - - /** - * Holds if method `name` of struct `Collection` from package - * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo) - * interprets parameter `n` as a query. - */ - private predicate mongoDbCollectionMethod(string name, int n) { - // func (coll *Collection) CountDocuments(ctx context.Context, filter interface{}, - // opts ...*options.CountOptions) (int64, error) - name = "CountDocuments" and n = 1 - or - // func (coll *Collection) DeleteMany(ctx context.Context, filter interface{}, - // opts ...*options.DeleteOptions) (*DeleteResult, error) - name = "DeleteMany" and n = 1 - or - // func (coll *Collection) DeleteOne(ctx context.Context, filter interface{}, - // opts ...*options.DeleteOptions) (*DeleteResult, error) - name = "DeleteOne" and n = 1 - or - // func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter interface{}, - // ...) ([]interface{}, error) - name = "Distinct" and n = 2 - or - // func (coll *Collection) Find(ctx context.Context, filter interface{}, - // opts ...*options.FindOptions) (*Cursor, error) - name = "Find" and n = 1 - or - // func (coll *Collection) FindOne(ctx context.Context, filter interface{}, - // opts ...*options.FindOneOptions) *SingleResult - name = "FindOne" and n = 1 - or - // func (coll *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, ...) - // *SingleResult - name = "FindOneAndDelete" and n = 1 - or - // func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{}, - // replacement interface{}, ...) *SingleResult - name = "FindOneAndReplace" and n = 1 - or - // func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{}, - // update interface{}, ...) *SingleResult - name = "FindOneAndUpdate" and n = 1 - or - // func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{}, - // replacement interface{}, ...) (*UpdateResult, error) - name = "ReplaceOne" and n = 1 - or - // func (coll *Collection) UpdateMany(ctx context.Context, filter interface{}, - // update interface{}, ...) (*UpdateResult, error) - name = "UpdateMany" and n = 1 - or - // func (coll *Collection) UpdateOne(ctx context.Context, filter interface{}, - // update interface{}, ...) (*UpdateResult, error) - name = "UpdateOne" and n = 1 - or - // func (coll *Collection) Watch(ctx context.Context, pipeline interface{}, ...) - // (*ChangeStream, error) - name = "Watch" and n = 1 - or - // func (coll *Collection) Aggregate(ctx context.Context, pipeline interface{}, - // opts ...*options.AggregateOptions) (*Cursor, error) - name = "Aggregate" and n = 1 - } - - /** - * A query used in an API function acting on a `Collection` struct of package - * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo). - */ - private class MongoDbCollectionQuery extends Range { - MongoDbCollectionQuery() { - exists(Method meth, string methodName, int n | - mongoDbCollectionMethod(methodName, n) and - meth.hasQualifiedName(package("go.mongodb.org/mongo-driver", "mongo"), "Collection", - methodName) and - this = meth.getACall().getArgument(n) - ) - } - } - } - - /** - * Holds if taint flows from `pred` to `succ` through a MongoDB-specific API. - */ - predicate isAdditionalMongoTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - // Taint an entry if the `Value` is tainted - exists(Write w, DataFlow::Node base, Field f | w.writesField(base, f, pred) | - base = succ.(DataFlow::PostUpdateNode).getPreUpdateNode() and - base.getType().hasQualifiedName(package("go.mongodb.org/mongo-driver", "bson/primitive"), "E") and - f.getName() = "Value" - ) - } -} diff --git a/ql/lib/semmle/go/frameworks/Protobuf.qll b/ql/lib/semmle/go/frameworks/Protobuf.qll deleted file mode 100644 index 66b3bba8d..000000000 --- a/ql/lib/semmle/go/frameworks/Protobuf.qll +++ /dev/null @@ -1,178 +0,0 @@ -/** Provides models of commonly used functions and types in the protobuf packages. */ - -import go - -/** Provides models of commonly used functions and types in the protobuf packages. */ -module Protobuf { - /** Gets the name of the modern protobuf top-level implementation package. */ - string modernProtobufPackage() { result = package("google.golang.org/protobuf", "proto") } - - /** Gets the name of the modern protobuf implementation's `protoiface` subpackage. */ - string protobufIfacePackage() { - result = package("google.golang.org/protobuf", "runtime/protoiface") - } - - /** Gets the name of the modern protobuf implementation's `protoreflect` subpackage. */ - string protobufReflectPackage() { - result = package("google.golang.org/protobuf", "reflect/protoreflect") - } - - /** Gets the name of a top-level protobuf implementation package. */ - string protobufPackages() { - result in [package("github.com/golang/protobuf", "proto"), modernProtobufPackage()] - } - - /** The `Marshal` and `MarshalAppend` functions in the protobuf packages. */ - private class MarshalFunction extends TaintTracking::FunctionModel, MarshalingFunction::Range { - string name; - - MarshalFunction() { - name = ["Marshal", "MarshalAppend"] and - ( - this.hasQualifiedName(protobufPackages(), name) or - this.(Method).hasQualifiedName(modernProtobufPackage(), "MarshalOptions", name) - ) - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - - override DataFlow::FunctionInput getAnInput() { - if name = "MarshalAppend" then result.isParameter(1) else result.isParameter(0) - } - - override DataFlow::FunctionOutput getOutput() { - name = "MarshalAppend" and result.isParameter(0) - or - result.isResult(0) - } - - override string getFormat() { result = "protobuf" } - } - - private Field inputMessageField() { - result.hasQualifiedName(protobufIfacePackage(), "MarshalInput", "Message") - } - - private Method marshalStateMethod() { - result.hasQualifiedName(protobufIfacePackage(), "MarshalOptions", "MarshalState") - } - - /** - * Additional taint-flow step modeling flow from `MarshalInput.Message` to `MarshalOutput`, - * mediated by a `MarshalOptions.MarshalState` call. - * - * Note we can taint the whole `MarshalOutput` as it only has one field (`Buf`), and taint- - * tracking always considers a field of a tainted struct to itself be tainted. - */ - private class MarshalStateStep extends TaintTracking::AdditionalTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::PostUpdateNode marshalInput, DataFlow::CallNode marshalStateCall | - marshalStateCall = marshalStateMethod().getACall() and - // pred -> marshalInput.Message - any(DataFlow::Write w) - .writesField(marshalInput.getPreUpdateNode(), inputMessageField(), pred) and - // marshalInput -> marshalStateCall - marshalStateCall.getArgument(0) = globalValueNumber(marshalInput).getANode() and - // marshalStateCall -> succ - marshalStateCall.getResult() = succ - ) - } - } - - /** The `Unmarshal` function in the protobuf packages. */ - class UnmarshalFunction extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { - UnmarshalFunction() { - this.hasQualifiedName(protobufPackages(), "Unmarshal") or - this.(Method).hasQualifiedName(modernProtobufPackage(), "UnmarshalOptions", "Unmarshal") - } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = this.getAnInput() and outp = this.getOutput() - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - - override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } - - override string getFormat() { result = "protobuf" } - } - - /** The `Merge` function in the protobuf packages. */ - private class MergeFunction extends TaintTracking::FunctionModel { - MergeFunction() { this.hasQualifiedName(protobufPackages(), "Merge") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isParameter(1) and outp.isParameter(0) - } - } - - /** A protobuf `Message` type. */ - class MessageType extends Type { - MessageType() { this.implements(protobufReflectPackage(), "ProtoMessage") } - } - - /** The `Clone` function in the protobuf packages. */ - private class MessageCloneFunction extends TaintTracking::FunctionModel { - MessageCloneFunction() { this.hasQualifiedName(protobufPackages(), "Clone") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isParameter(0) and outp.isResult() - } - } - - /** A `Get` method of a protobuf `Message` type. */ - private class GetMethod extends DataFlow::FunctionModel, Method { - GetMethod() { - exists(string name | name.matches("Get%") | this = any(MessageType msg).getMethod(name)) - } - - override predicate hasDataFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - /** A `ProtoReflect` method of a protobuf `Message` type. */ - private class ProtoReflectMethod extends DataFlow::FunctionModel, Method { - ProtoReflectMethod() { this = any(MessageType msg).getMethod("ProtoReflect") } - - override predicate hasDataFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - /** - * Gets the base of `node`, looking through any dereference node found. - */ - private DataFlow::Node getBaseLookingThroughDerefs(DataFlow::ComponentReadNode node) { - result = node.getBase().(DataFlow::PointerDereferenceNode).getOperand() - or - result = node.getBase() and not node.getBase() instanceof DataFlow::PointerDereferenceNode - } - - /** - * Gets the data-flow node representing the bottom of a stack of zero or more `ComponentReadNode`s - * perhaps with interleaved dereferences. - * - * For example, in the expression a.b[c].d[e], this would return the dataflow node for the read from `a`. - */ - private DataFlow::Node getUnderlyingNode(DataFlow::ReadNode read) { - (result = read or result = getBaseLookingThroughDerefs+(read)) and - not result instanceof DataFlow::ComponentReadNode - } - - /** - * Additional taint step tainting a Message when taint is written to any of its fields and/or elements. - */ - private class WriteMessageFieldStep extends TaintTracking::AdditionalTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - [succ.getType(), succ.getType().getPointerType()] instanceof MessageType and - exists(DataFlow::ReadNode base | - succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = getUnderlyingNode(base) - | - any(DataFlow::Write w).writesComponent(base, pred) - ) - } - } -} diff --git a/ql/lib/semmle/go/frameworks/Revel.qll b/ql/lib/semmle/go/frameworks/Revel.qll deleted file mode 100644 index 1ccad431b..000000000 --- a/ql/lib/semmle/go/frameworks/Revel.qll +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Provides classes for working with untrusted flow sources from the `github.com/revel/revel` package. - */ - -import go -private import semmle.go.security.OpenUrlRedirectCustomizations - -/** Provides classes and methods modeling the Revel web framework. */ -module Revel { - /** Gets the package name `github.com/revel/revel`. */ - string packagePath() { result = package(["github.com/revel", "github.com/robfig"], "revel") } - - private class ControllerParams extends UntrustedFlowSource::Range, DataFlow::FieldReadNode { - ControllerParams() { - exists(Field f | - this.readsField(_, f) and - f.hasQualifiedName(packagePath(), "Controller", "Params") - ) - } - } - - private class ParamsFixedSanitizer extends TaintTracking::DefaultTaintSanitizer, - DataFlow::FieldReadNode { - ParamsFixedSanitizer() { - exists(Field f | - this.readsField(_, f) and - f.hasQualifiedName(packagePath(), "Params", "Fixed") - ) - } - } - - private class ParamsBind extends TaintTracking::FunctionModel, Method { - ParamsBind() { this.hasQualifiedName(packagePath(), "Params", ["Bind", "BindJSON"]) } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isParameter(0) - } - } - - private class RouteMatchParams extends UntrustedFlowSource::Range, DataFlow::FieldReadNode { - RouteMatchParams() { - exists(Field f | - this.readsField(_, f) and - f.hasQualifiedName(packagePath(), "RouteMatch", "Params") - ) - } - } - - /** An access to an HTTP request field whose value may be controlled by an untrusted user. */ - private class UserControlledRequestField extends UntrustedFlowSource::Range, - DataFlow::FieldReadNode { - UserControlledRequestField() { - exists(string fieldName | - this.getField().hasQualifiedName(packagePath(), "Request", fieldName) - | - fieldName in [ - "Header", "ContentType", "AcceptLanguages", "Locale", "URL", "Form", "MultipartForm" - ] - ) - } - } - - private class UserControlledRequestMethod extends UntrustedFlowSource::Range, - DataFlow::MethodCallNode { - UserControlledRequestMethod() { - this.getTarget() - .hasQualifiedName(packagePath(), "Request", - [ - "FormValue", "PostFormValue", "GetQuery", "GetForm", "GetMultipartForm", "GetBody", - "Cookie", "GetHttpHeader", "GetRequestURI", "MultipartReader", "Referer", "UserAgent" - ]) - } - } - - private class ServerCookieGetValue extends TaintTracking::FunctionModel, Method { - ServerCookieGetValue() { this.implements(packagePath(), "ServerCookie", "GetValue") } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private class ServerMultipartFormGetFiles extends TaintTracking::FunctionModel, Method { - ServerMultipartFormGetFiles() { - this.implements(packagePath(), "ServerMultipartForm", ["GetFiles", "GetValues"]) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isResult() - } - } - - private string contentTypeFromFilename(DataFlow::Node filename) { - if filename.getStringValue().toLowerCase().matches(["%.htm", "%.html"]) - then result = "text/html" - else result = "application/octet-stream" - // Actually Revel can figure out a variety of other content-types, but none of our analyses care to - // distinguish ones other than text/html. - } - - /** - * `revel.Controller` methods which set the response content-type to and designate a result in one operation. - * - * Note these don't actually generate the response, they return a struct which is then returned by the controller - * method, but it is very likely if a string is being rendered that it will end up sent to the user. - * - * The `Render` and `RenderTemplate` methods are handled by `TemplateRender` below. - * - * The `RenderError` method can actually return HTML content, but again only via an HTML template if one exists; - * we assume it falls back to return plain text as this implies there is probably not an injection opportunity - * but there is an information leakage issue. - * - * The `RenderBinary` method can also return a variety of content-types based on the file extension passed. - * We look particularly for html file extensions, since these are the only ones we currently have special rules - * for (in particular, detecting XSS vulnerabilities). - */ - private class ControllerRenderMethods extends HTTP::ResponseBody::Range { - string contentType; - - ControllerRenderMethods() { - exists(Method m, string methodName, DataFlow::CallNode methodCall | - m.hasQualifiedName(packagePath(), "Controller", methodName) and - methodCall = m.getACall() - | - exists(int exposedArgument | - this = methodCall.getArgument(exposedArgument) and - ( - methodName = "RenderBinary" and - contentType = contentTypeFromFilename(methodCall.getArgument(1)) and - exposedArgument = 0 - or - methodName = "RenderError" and contentType = "text/plain" and exposedArgument = 0 - or - methodName = "RenderHTML" and contentType = "text/html" and exposedArgument = 0 - or - methodName = "RenderJSON" and contentType = "application/json" and exposedArgument = 0 - or - methodName = "RenderJSONP" and - contentType = "application/javascript" and - exposedArgument = 1 - or - methodName = "RenderXML" and contentType = "text/xml" and exposedArgument = 0 - ) - ) - or - methodName = "RenderText" and - contentType = "text/plain" and - this = methodCall.getAnArgument() - ) - } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - override string getAContentType() { result = contentType } - } - - /** - * The `revel.Controller.RenderFileName` method, which instructs Revel to open a file and return its contents. - * We extend FileSystemAccess rather than HTTP::ResponseBody as this will usually mean exposing a user-controlled - * file rather than the actual contents being user-controlled. - */ - private class RenderFileNameCall extends FileSystemAccess::Range, DataFlow::CallNode { - RenderFileNameCall() { - this = - any(Method m | m.hasQualifiedName(packagePath(), "Controller", "RenderFileName")).getACall() - } - - override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - } - - /** - * The `revel.Controller.Redirect` method. - * - * It is currently assumed that a tainted `value` in `Redirect(url, value)`, which calls `Sprintf(url, value)` - * internally, cannot lead to an open redirect vulnerability. - */ - private class ControllerRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode { - ControllerRedirectMethod() { - exists(Method m | m.hasQualifiedName(packagePath(), "Controller", "Redirect") | - this = m.getACall() - ) - } - - override DataFlow::Node getUrl() { result = this.getArgument(0) } - - override HTTP::ResponseWriter getResponseWriter() { none() } - } - - /** - * The getter and setter methods of `revel.RevelHeader`. - * - * Note we currently don't implement `HeaderWrite` and related concepts, as they are currently only used - * to track content-type, and directly setting headers does not seem to be the usual way to set the response - * content-type for this framework. If and when the `HeaderWrite` concept has a more abstract idea of the - * relationship between header-writes and HTTP responses than looking for a particular `http.ResponseWriter` - * instance connecting the two, then we may implement it here for completeness. - */ - private class RevelHeaderMethods extends TaintTracking::FunctionModel { - FunctionInput input; - FunctionOutput output; - string name; - - RevelHeaderMethods() { - this.(Method).hasQualifiedName(packagePath(), "RevelHeader", name) and - ( - name = ["Add", "Set"] and input.isParameter([0, 1]) and output.isReceiver() - or - name = ["Get", "GetAll"] and input.isReceiver() and output.isResult() - or - name = "SetCookie" and input.isParameter(0) and output.isReceiver() - ) - } - - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp = input and outp = output - } - } - - /** - * A read in a Revel template that uses Revel's `raw` function. - */ - class RawTemplateRead extends HtmlTemplate::TemplateRead { - RawTemplateRead() { parent.getBody().regexpMatch("(?s)raw\\s.*") } - } - - /** - * A write to a template argument field that is read raw inside of a template. - */ - private class RawTemplateArgument extends HTTP::TemplateResponseBody::Range { - RawTemplateRead read; - - RawTemplateArgument() { - exists(TemplateRender render, VariableWithFields var | - render.getRenderedFile() = read.getFile() and - // if var is a.b.c, any rhs of a write to a, a.b, or a.b.cb - this = var.getParent*().getAWrite().getRhs() - | - var.getParent*() = render.getArgumentVariable() and - ( - var = read.getReadVariable(render.getArgumentVariable()) - or - // if no write or use of that variable exists, no VariableWithFields will be generated - // so we try to find a parent VariableWithFields - // this isn't covered by the 'getParent*' above because no match would be found at all - // for var - not exists(read.getReadVariable(render.getArgumentVariable())) and - exists(string fieldName | fieldName = read.getFieldName() | - var.getQualifiedName() = - render.getArgumentVariable().getQualifiedName() + - ["." + fieldName.substring(0, fieldName.indexOf(".")), ""] - ) - ) - or - // a revel controller.Render(arg) will set controller.ViewArgs["arg"] = arg - exists(Variable arg | arg.getARead() = render.(ControllerRender).getAnArgument() | - var.getBaseVariable() = arg and - var.getQualifiedName() = read.getFieldName() - ) - ) - } - - override string getAContentType() { result = "text/html" } - - override HTTP::ResponseWriter getResponseWriter() { none() } - - override HtmlTemplate::TemplateRead getRead() { result = read } - } - - /** - * A render of a template. - */ - abstract class TemplateRender extends DataFlow::Node, TemplateInstantiation::Range { - /** Gets the name of the file that is rendered. */ - abstract File getRenderedFile(); - - /** Gets the variable passed as an argument to the template. */ - abstract VariableWithFields getArgumentVariable(); - - override DataFlow::Node getADataArgument() { result = this.getArgumentVariable().getAUse() } - } - - private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) { - result = insn or - result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase()) - } - - /** A call to `Controller.Render`. */ - private class ControllerRender extends TemplateRender, DataFlow::MethodCallNode { - ControllerRender() { this.getTarget().hasQualifiedName(packagePath(), "Controller", "Render") } - - override DataFlow::Node getTemplateArgument() { none() } - - override File getRenderedFile() { - exists(Type controllerType, string controllerRe, string handlerRe, string pathRe | - controllerType = skipImplicitFieldReads(this.getReceiver().asInstruction()).getResultType() and - controllerRe = "\\Q" + controllerType.getName() + "\\E" and - handlerRe = "\\Q" + this.getRoot().(FuncDef).getName() + "\\E" and - // find a file named '/views//(.