Compare commits

..

6 Commits

Author SHA1 Message Date
Michael B. Gale
31d7d6fc55 Go: Update test expectations 2024-10-07 13:30:46 +01:00
Michael B. Gale
dda745f326 Go: Only emit no packages found error if no Go files were found 2024-10-07 13:28:45 +01:00
Michael B. Gale
1f8e82148d Go: Communicate extracted package count from extractor to autobuilder 2024-10-07 12:54:50 +01:00
Michael B. Gale
a7bb4668f0 Go: Add integration test with no extracted packages across multiple modules 2024-10-07 12:32:24 +01:00
Michael B. Gale
f61635a08c Add test for module without packages, but sources, in workspace 2024-10-07 12:29:12 +01:00
Michael B. Gale
22f6af8145 Go: Add warning-level diagnostic for no files extracted 2024-10-03 11:39:02 +01:00
1749 changed files with 93443 additions and 144840 deletions

View File

@@ -1,4 +1,5 @@
common --enable_platform_specific_config
common --enable_bzlmod
# because we use --override_module with `%workspace%`, the lock file is not stable
common --lockfile_mode=off

View File

@@ -1 +1 @@
8.0.0rc1
5f5d70b6c4d2fb1a889479569107f1692239e8a7

View File

@@ -9,5 +9,3 @@ paths-ignore:
- '/python/'
- '/javascript/ql/test'
- '/javascript/extractor/tests'
- '/rust/ql/test'
- '/rust/ql/integration-tests'

4
.github/labeler.yml vendored
View File

@@ -38,10 +38,6 @@ Swift:
- swift/**/*
- change-notes/**/*swift*
Actions:
- actions/**/*
- change-notes/**/*actions*
documentation:
- "**/*.qhelp"
- "**/*.md"

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-13, windows-2019]
os: [ubuntu-20.04, macos-12, windows-2019]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

View File

@@ -1,64 +0,0 @@
name: "Code scanning - Rust"
on:
push:
branches:
- main
- 'rc/*'
pull_request:
branches:
- main
- 'rc/*'
paths:
- '**/*.rs'
- '**/Cargo.toml'
- '.github/codeql/codeql-config.yml'
- '.github/workflows/rust-analysis.yml'
schedule:
- cron: '0 9 * * 1'
env:
CODEQL_ENABLE_EXPERIMENTAL_FEATURES: "true"
jobs:
analyze:
strategy:
matrix:
language: [ 'rust' ]
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Query latest nightly CodeQL bundle
shell: bash
id: codeql
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
REPO=dsp-testing/codeql-cli-nightlies
TAG=$(
gh release list -R $REPO -L1 --exclude-drafts --json tagName -q ".[] | .tagName"
)
echo "nightly_bundle=https://github.com/$REPO/releases/download/$TAG/codeql-bundle-linux64.tar.zst" \
| tee -a "$GITHUB_OUTPUT"
- name: Initialize CodeQL
uses: github/codeql-action/init@main
with:
tools: ${{ steps.codeql.outputs.nightly_bundle }}
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
- name: Autobuild
uses: github/codeql-action/autobuild@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@main

View File

@@ -44,7 +44,7 @@ jobs:
# without waiting for the macOS build
build-and-test-macos:
if: github.repository_owner == 'github'
runs-on: macos-13-xlarge
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v4
- uses: ./swift/actions/build-and-test
@@ -64,7 +64,7 @@ jobs:
qltests-macos:
if: ${{ github.repository_owner == 'github' && github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-13-xlarge
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v4
- uses: ./swift/actions/run-ql-tests

View File

@@ -23,6 +23,7 @@
/ql/ @github/codeql-ql-for-ql-reviewers
# Bazel (excluding BUILD.bazel files)
WORKSPACE.bazel @github/codeql-ci-reviewers
MODULE.bazel @github/codeql-ci-reviewers
.bazelversion @github/codeql-ci-reviewers
.bazelrc @github/codeql-ci-reviewers

49
Cargo.lock generated
View File

@@ -112,16 +112,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "ast-generator"
version = "0.1.0"
dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
"ungrammar",
]
[[package]]
name = "atomic"
version = "0.6.0"
@@ -384,20 +374,17 @@ dependencies = [
"clap",
"codeql-extractor",
"figment",
"glob",
"itertools 0.13.0",
"log",
"num-traits",
"ra_ap_base_db",
"ra_ap_hir",
"ra_ap_hir_def",
"ra_ap_hir_expand",
"ra_ap_ide_db",
"ra_ap_load-cargo",
"ra_ap_parser",
"ra_ap_paths",
"ra_ap_project_model",
"ra_ap_span",
"ra_ap_syntax",
"ra_ap_vfs",
"rust-extractor-macros",
@@ -632,7 +619,6 @@ dependencies = [
"atomic",
"pear",
"serde",
"serde_yaml",
"uncased",
"version_check",
]
@@ -695,6 +681,16 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
[[package]]
name = "generate-schema"
version = "0.1.0"
dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
"ungrammar",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -706,12 +702,6 @@ dependencies = [
"wasi",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
version = "0.4.15"
@@ -2066,19 +2056,6 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.5.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -2402,12 +2379,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "utf8parse"
version = "0.2.2"

View File

@@ -7,7 +7,7 @@ members = [
"ruby/extractor",
"rust/extractor",
"rust/extractor/macros",
"rust/ast-generator",
"rust/generate-schema",
]
[patch.crates-io]

View File

@@ -18,16 +18,16 @@ bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_go", version = "0.50.0")
bazel_dep(name = "rules_pkg", version = "1.0.1")
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
bazel_dep(name = "rules_python", version = "0.36.0")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "rules_python", version = "0.35.0")
bazel_dep(name = "bazel_skylib", version = "1.6.1")
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "10.0.0")
bazel_dep(name = "rules_kotlin", version = "2.0.0-codeql.1")
bazel_dep(name = "rules_kotlin", version = "1.9.4-codeql.1")
bazel_dep(name = "gazelle", version = "0.38.0")
bazel_dep(name = "rules_dotnet", version = "0.16.1")
bazel_dep(name = "rules_dotnet", version = "0.15.1")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
bazel_dep(name = "rules_rust", version = "0.52.2")
bazel_dep(name = "rules_rust", version = "0.50.0")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
@@ -61,7 +61,7 @@ r.from_cargo(
"//ruby/extractor:Cargo.toml",
"//rust/extractor:Cargo.toml",
"//rust/extractor/macros:Cargo.toml",
"//rust/ast-generator:Cargo.toml",
"//rust/generate-schema:Cargo.toml",
"//shared/tree-sitter-extractor:Cargo.toml",
],
)

2
WORKSPACE.bazel Normal file
View File

@@ -0,0 +1,2 @@
# please use MODULE.bazel to add dependencies
# this empty file is required by internal repositories, don't remove it

View File

@@ -1,20 +0,0 @@
load("//misc/bazel:pkg.bzl", "codeql_pack")
package(default_visibility = ["//visibility:public"])
[
codeql_pack(
name = "-".join(parts),
srcs = [
"//actions/extractor",
],
pack_prefix = "/".join(parts),
)
for parts in (
[
"experimental",
"actions",
],
["actions"],
)
]

View File

@@ -1,10 +0,0 @@
load("//misc/bazel:pkg.bzl", "codeql_pkg_files", "strip_prefix")
codeql_pkg_files(
name = "extractor",
srcs = [
"codeql-extractor.yml",
] + glob(["tools/**"]),
strip_prefix = strip_prefix.from_pkg(),
visibility = ["//actions:__pkg__"],
)

View File

@@ -1,44 +0,0 @@
name: "actions"
aliases: []
display_name: "GitHub Actions"
version: 0.0.1
column_kind: "utf16"
unicode_newlines: true
build_modes:
- none
file_coverage_languages: []
github_api_languages: []
scc_languages: []
file_types:
- name: workflow
display_name: GitHub Actions workflow files
extensions:
- .yml
- .yaml
forwarded_extractor_name: javascript
options:
trap:
title: TRAP options
description: Options about how the extractor handles TRAP files
type: object
visibility: 3
properties:
cache:
title: TRAP cache options
description: Options about how the extractor handles its TRAP cache
type: object
properties:
dir:
title: TRAP cache directory
description: The directory of the TRAP cache to use
type: string
bound:
title: TRAP cache bound
description: A soft limit (in MB) on the size of the TRAP cache
type: string
pattern: "[0-9]+"
write:
title: TRAP cache writeable
description: Whether to write to the TRAP cache as well as reading it
type: string
pattern: "(true|TRUE|false|FALSE)"

View File

@@ -1,40 +0,0 @@
if (($null -ne $env:LGTM_INDEX_INCLUDE) -or ($null -ne $env:LGTM_INDEX_EXCLUDE) -or ($null -ne $env:LGTM_INDEX_FILTERS)) {
Write-Output 'Path filters set. Passing them through to the JavaScript extractor.'
} else {
Write-Output 'No path filters set. Using the default filters.'
$DefaultPathFilters = @(
'exclude:**/*',
'include:.github/workflows/**/*.yml',
'include:.github/workflows/**/*.yaml',
'include:**/action.yml',
'include:**/action.yaml'
)
$env:LGTM_INDEX_FILTERS = $DefaultPathFilters -join "`n"
}
# Find the JavaScript extractor directory via `codeql resolve extractor`.
$CodeQL = Join-Path $env:CODEQL_DIST 'codeql.exe'
$env:CODEQL_EXTRACTOR_JAVASCRIPT_ROOT = &$CodeQL resolve extractor --language javascript
if ($LASTEXITCODE -ne 0) {
throw 'Failed to resolve JavaScript extractor.'
}
Write-Output "Found JavaScript extractor at '${env:CODEQL_EXTRACTOR_JAVASCRIPT_ROOT}'."
# Run the JavaScript autobuilder.
$JavaScriptAutoBuild = Join-Path $env:CODEQL_EXTRACTOR_JAVASCRIPT_ROOT 'tools\autobuild.cmd'
Write-Output "Running JavaScript autobuilder at '${JavaScriptAutoBuild}'."
# Copy the values of the Actions extractor environment variables to the JavaScript extractor environment variables.
$env:CODEQL_EXTRACTOR_JAVASCRIPT_DIAGNOSTIC_DIR = $env:CODEQL_EXTRACTOR_ACTIONS_DIAGNOSTIC_DIR
$env:CODEQL_EXTRACTOR_JAVASCRIPT_LOG_DIR = $env:CODEQL_EXTRACTOR_ACTIONS_LOG_DIR
$env:CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR = $env:CODEQL_EXTRACTOR_ACTIONS_SCRATCH_DIR
$env:CODEQL_EXTRACTOR_JAVASCRIPT_SOURCE_ARCHIVE_DIR = $env:CODEQL_EXTRACTOR_ACTIONS_SOURCE_ARCHIVE_DIR
$env:CODEQL_EXTRACTOR_JAVASCRIPT_TRAP_DIR = $env:CODEQL_EXTRACTOR_ACTIONS_TRAP_DIR
$env:CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE = $env:CODEQL_EXTRACTOR_ACTIONS_WIP_DATABASE
&$JavaScriptAutoBuild
if ($LASTEXITCODE -ne 0) {
throw "JavaScript autobuilder failed."
}

View File

@@ -1,3 +0,0 @@
@echo off
rem All of the work is done in the PowerShell script
powershell.exe %~dp0autobuild-impl.ps1

View File

@@ -1,39 +0,0 @@
#!/bin/sh
set -eu
DEFAULT_PATH_FILTERS=$(cat << END
exclude:**/*
include:.github/workflows/**/*.yml
include:.github/workflows/**/*.yaml
include:**/action.yml
include:**/action.yaml
END
)
if [ -n "${LGTM_INDEX_INCLUDE:-}" ] || [ -n "${LGTM_INDEX_EXCLUDE:-}" ] || [ -n "${LGTM_INDEX_FILTERS:-}" ] ; then
echo "Path filters set. Passing them through to the JavaScript extractor."
else
echo "No path filters set. Using the default filters."
LGTM_INDEX_FILTERS="${DEFAULT_PATH_FILTERS}"
export LGTM_INDEX_FILTERS
fi
# Find the JavaScript extractor directory via `codeql resolve extractor`.
CODEQL_EXTRACTOR_JAVASCRIPT_ROOT="$($CODEQL_DIST/codeql resolve extractor --language javascript)"
export CODEQL_EXTRACTOR_JAVASCRIPT_ROOT
echo "Found JavaScript extractor at '${CODEQL_EXTRACTOR_JAVASCRIPT_ROOT}'."
# Run the JavaScript autobuilder
JAVASCRIPT_AUTO_BUILD="${CODEQL_EXTRACTOR_JAVASCRIPT_ROOT}/tools/autobuild.sh"
echo "Running JavaScript autobuilder at '${JAVASCRIPT_AUTO_BUILD}'."
# Copy the values of the Actions extractor environment variables to the JavaScript extractor environment variables.
env CODEQL_EXTRACTOR_JAVASCRIPT_DIAGNOSTIC_DIR="${CODEQL_EXTRACTOR_ACTIONS_DIAGNOSTIC_DIR}" \
CODEQL_EXTRACTOR_JAVASCRIPT_LOG_DIR="${CODEQL_EXTRACTOR_ACTIONS_LOG_DIR}" \
CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR="${CODEQL_EXTRACTOR_ACTIONS_SCRATCH_DIR}" \
CODEQL_EXTRACTOR_JAVASCRIPT_SOURCE_ARCHIVE_DIR="${CODEQL_EXTRACTOR_ACTIONS_SOURCE_ARCHIVE_DIR}" \
CODEQL_EXTRACTOR_JAVASCRIPT_TRAP_DIR="${CODEQL_EXTRACTOR_ACTIONS_TRAP_DIR}" \
CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE="${CODEQL_EXTRACTOR_ACTIONS_WIP_DATABASE}" \
${JAVASCRIPT_AUTO_BUILD}

View File

@@ -1 +0,0 @@
predicate placeholder(int x) { x = 0 }

View File

@@ -1,12 +0,0 @@
name: codeql/actions-all
version: 0.0.1-dev
library: true
warnOnImplicitThis: true
dependencies:
codeql/util: ${workspace}
codeql/yaml: ${workspace}
codeql/controlflow: ${workspace}
codeql/dataflow: ${workspace}
codeql/javascript-all: ${workspace}
extractor: actions
groups: actions

View File

@@ -1,16 +0,0 @@
/**
* @name Placeholder Query
* @description Placeholder
* @kind problem
* @problem.severity warning
* @security-severity 9.3
* @precision high
* @id actions/placeholder
* @tags actions security
*/
import actions
import javascript
from File f
select f, "Analyzed a file."

View File

@@ -1,8 +0,0 @@
name: codeql/actions-queries
version: 0.0.1-dev
library: false
groups: [actions, queries]
extractor: actions
dependencies:
codeql/actions-all: ${workspace}
warnOnImplicitThis: true

View File

@@ -1,23 +0,0 @@
on: push
jobs:
job1:
runs-on: ubuntu-latest
steps:
- shell: pwsh
run: Write-Output "foo"
job2:
runs-on: ubuntu-latest
steps:
- run: echo "foo"
job3:
runs-on: windows-latest
steps:
- shell: bash
run: echo "foo"
job4:
runs-on: windows-latest
steps:
- run: Write-Output "foo"

View File

@@ -1 +0,0 @@
| 1 |

View File

@@ -1 +0,0 @@
select 1

View File

@@ -1,8 +0,0 @@
name: codeql/actions-tests
groups: [codeql, test]
dependencies:
codeql/actions-all: ${workspace}
codeql/actions-queries: ${workspace}
extractor: actions
tests: .
warnOnImplicitThis: true

View File

@@ -1,23 +0,0 @@
on: push
jobs:
job1:
runs-on: ubuntu-latest
steps:
- shell: pwsh
run: Write-Output "foo"
job2:
runs-on: ubuntu-latest
steps:
- run: echo "foo"
job3:
runs-on: windows-latest
steps:
- shell: bash
run: echo "foo"
job4:
runs-on: windows-latest
steps:
- run: Write-Output "foo"

View File

@@ -1 +0,0 @@
| .github/workflows/shell.yml:0:0:0:0 | .github/workflows/shell.yml | Analyzed a file. |

View File

@@ -1 +0,0 @@
Placeholder.ql

View File

@@ -351,5 +351,9 @@
"Python model summaries test extension": [
"python/ql/test/library-tests/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/library-tests/dataflow/model-summaries/NormalDataflowTest.ext.yml"
],
"Diagnostics.qll": [
"ruby/ql/lib/codeql/ruby/Diagnostics.qll",
"rust/ql/lib/codeql/rust/Diagnostics.qll"
]
}

View File

@@ -1,15 +0,0 @@
class Expr extends @expr {
string toString() { none() }
}
class Location extends @location_expr {
string toString() { none() }
}
predicate isExprRequires(Expr expr) { exists(int kind | exprs(expr, kind, _) | kind = 390) }
from Expr expr, int kind, int kind_new, Location location
where
exprs(expr, kind, location) and
if isExprRequires(expr) then kind_new = 1 else kind_new = kind
select expr, kind_new, location

View File

@@ -1,3 +0,0 @@
description: Add requires expr
compatibility: partial
exprs.rel: run exprs.qlo

View File

@@ -1,17 +0,0 @@
class Expr extends @expr {
string toString() { none() }
}
class Location extends @location_expr {
string toString() { none() }
}
predicate isExprRequirement(Expr expr) {
exists(int kind | exprs(expr, kind, _) | kind = [391, 392, 393])
}
from Expr expr, int kind, int kind_new, Location location
where
exprs(expr, kind, location) and
if isExprRequirement(expr) then kind_new = 1 else kind_new = kind
select expr, kind_new, location

View File

@@ -1,17 +0,0 @@
class Parameter extends @parameter {
string toString() { none() }
}
class ParameterizedElement extends @parameterized_element {
string toString() { none() }
}
class Type extends @type {
string toString() { none() }
}
from Parameter param, ParameterizedElement pe, int index, Type type
where
params(param, pe, index, type) and
not pe instanceof @requires_expr
select param, pe, index, type

View File

@@ -1,5 +0,0 @@
description: Support C++20 requires expressions
compatibility: partial
compound_requirement_is_noexcept.rel: delete
exprs.rel: run exprs.qlo
params.rel: run params.qlo

View File

@@ -1,23 +1,3 @@
## 2.1.0
### New Features
* Added a new predicate `DataFlow::getARuntimeTarget` for getting a function that may be invoked by a `Call` expression. Unlike `Call.getTarget` this new predicate may also resolve function pointers.
* Added the predicate `mayBeFromImplicitlyDeclaredFunction()` to the `Call` class to represent calls that may be the return value of an implicitly declared C function.
* Added the predicate `getAnExplicitDeclarationEntry()` to the `Function` class to get a `FunctionDeclarationEntry` that is not implicit.
* Added classes `RequiresExpr`, `SimpleRequirementExpr`, `TypeRequirementExpr`, `CompoundRequirementExpr`, and `NestedRequirementExpr` to represent C++20 requires expressions and the simple, type, compound, and nested requirements that can occur in `requires` expressions.
### Minor Analysis Improvements
* The function call target resolution algorithm has been improved to resolve more calls through function pointers. As a result, dataflow queries may have more results.
## 2.0.2
### Minor Analysis Improvements
* Added taint flow model for `fopen` and related functions.
* The `SimpleRangeAnalysis` library (`semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis`) now generates more precise ranges for calls to `fgetc` and `getc`.
## 2.0.1
No user-facing changes.

View File

@@ -1,6 +0,0 @@
## 2.0.2
### Minor Analysis Improvements
* Added taint flow model for `fopen` and related functions.
* The `SimpleRangeAnalysis` library (`semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis`) now generates more precise ranges for calls to `fgetc` and `getc`.

View File

@@ -1,12 +0,0 @@
## 2.1.0
### New Features
* Added a new predicate `DataFlow::getARuntimeTarget` for getting a function that may be invoked by a `Call` expression. Unlike `Call.getTarget` this new predicate may also resolve function pointers.
* Added the predicate `mayBeFromImplicitlyDeclaredFunction()` to the `Call` class to represent calls that may be the return value of an implicitly declared C function.
* Added the predicate `getAnExplicitDeclarationEntry()` to the `Function` class to get a `FunctionDeclarationEntry` that is not implicit.
* Added classes `RequiresExpr`, `SimpleRequirementExpr`, `TypeRequirementExpr`, `CompoundRequirementExpr`, and `NestedRequirementExpr` to represent C++20 requires expressions and the simple, type, compound, and nested requirements that can occur in `requires` expressions.
### Minor Analysis Improvements
* The function call target resolution algorithm has been improved to resolve more calls through function pointers. As a result, dataflow queries may have more results.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.1.0
lastReleaseVersion: 2.0.1

View File

@@ -17,7 +17,6 @@ import semmle.code.cpp.File
import semmle.code.cpp.Linkage
import semmle.code.cpp.Location
import semmle.code.cpp.Compilation
import semmle.code.cpp.Concept
import semmle.code.cpp.Element
import semmle.code.cpp.Namespace
import semmle.code.cpp.Specifier

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 2.1.0
version: 2.0.2-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -1,161 +0,0 @@
/**
* Provides classes for working with C++ concepts.
*/
import semmle.code.cpp.exprs.Expr
/**
* A C++ requires expression.
*
* For example, with `T` and `U` template parameters:
* ```cpp
* requires (T x, U y) { x + y; };
* ```
*/
class RequiresExpr extends Expr, @requires_expr {
override string toString() {
if exists(this.getAParameter())
then result = "requires(...) { ... }"
else result = "requires { ... }"
}
override string getAPrimaryQlClass() { result = "RequiresExpr" }
/**
* Gets a requirement in this requires expression.
*/
RequirementExpr getARequirement() { result = this.getAChild() }
/**
* Gets the nth requirement in this requires expression.
*/
RequirementExpr getRequirement(int n) { result = this.getChild(n) }
/**
* Gets the number of requirements in this requires expression.
*/
int getNumberOfRequirements() { result = count(this.getARequirement()) }
/**
* Gets a parameter of this requires expression, if any.
*/
Parameter getAParameter() { result.getRequiresExpr() = underlyingElement(this) }
/**
* Gets the the nth parameter of this requires expression.
*/
Parameter getParameter(int n) {
result.getRequiresExpr() = underlyingElement(this) and result.getIndex() = n
}
/**
* Gets the number of parameters of this requires expression.
*/
int getNumberOfParameters() { result = count(this.getAParameter()) }
}
/**
* A C++ requirement in a requires expression.
*/
class RequirementExpr extends Expr { }
/**
* A C++ simple requirement in a requires expression.
*
* For example, if:
* ```cpp
* requires(T x, U y) { x + y; };
* ```
* with `T` and `U` template parameters, then `x + y;` is a simple requirement.
*/
class SimpleRequirementExpr extends RequirementExpr {
SimpleRequirementExpr() {
this.getParent() instanceof RequiresExpr and
not this instanceof TypeRequirementExpr and
not this instanceof CompoundRequirementExpr and
not this instanceof NestedRequirementExpr
}
override string getAPrimaryQlClass() { result = "SimpleRequirementExpr" }
}
/**
* A C++ type requirement in a requires expression.
*
* For example, if:
* ```cpp
* requires { typename T::a_field; };
* ```
* with `T` a template parameter, then `typename T::a_field;` is a type requirement.
*/
class TypeRequirementExpr extends RequirementExpr, TypeName {
TypeRequirementExpr() { this.getParent() instanceof RequiresExpr }
override string getAPrimaryQlClass() { result = "TypeRequirementExpr" }
}
/**
* A C++ compound requirement in a requires expression.
*
* For example, if:
* ```cpp
* requires(T x) { { x } noexcept -> std::same_as<int>; };
* ```
* with `T` a template parameter, then `{ x } noexcept -> std::same_as<int>;` is
* a compound requirement.
*/
class CompoundRequirementExpr extends RequirementExpr, @compound_requirement {
override string toString() {
if exists(this.getReturnTypeRequirement())
then result = "{ ... } -> ..."
else result = "{ ... }"
}
override string getAPrimaryQlClass() { result = "CompoundRequirementExpr" }
/**
* Gets the expression from the compound requirement.
*/
Expr getExpr() { result = this.getChild(0) }
/**
* Gets the return type requirement from the compound requirement, if any.
*/
Expr getReturnTypeRequirement() { result = this.getChild(1) }
/**
* Holds if the expression from the compound requirement must not be
* potentially throwing.
*/
predicate isNoExcept() { compound_requirement_is_noexcept(underlyingElement(this)) }
}
/**
* A C++ nested requirement in a requires expression.
*
* For example, if:
* ```cpp
* requires { requires std::is_same<T, int>::value; };
* ```
* with `T` a template parameter, then `requires std::is_same<T, int>::value;` is
* a nested requirement.
*/
class NestedRequirementExpr extends Expr, @nested_requirement {
override string toString() { result = "requires ..." }
override string getAPrimaryQlClass() { result = "NestedRequirementExpr" }
/**
* Gets the constraint from the nested requirement.
*/
Expr getConstraint() { result = this.getChild(0) }
}
/**
* A C++ concept id expression.
*/
class ConceptIdExpr extends RequirementExpr, @concept_id {
override string toString() { result = "concept<...>" }
override string getAPrimaryQlClass() { result = "ConceptIdExpr" }
}

View File

@@ -129,7 +129,7 @@ class Element extends ElementBase {
* or certain kinds of `Statement`.
*/
Element getParentScope() {
// result instanceof Class
// result instanceof class
exists(Declaration m |
m = this and
result = m.getDeclaringType() and
@@ -138,40 +138,31 @@ class Element extends ElementBase {
or
exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc)
or
// result instanceof Namespace
// result instanceof namespace
exists(Namespace n | result = n and n.getADeclaration() = this)
or
exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n)
or
exists(Namespace n | this = n and result = n.getParentNamespace())
or
// result instanceof Stmt
// result instanceof stmt
exists(LocalVariable v |
this = v and
exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent())
)
or
exists(Parameter p |
this = p and
(
result = p.getFunction() or
result = p.getCatchBlock().getParent().(Handler).getParent().(TryStmt).getParent() or
result = p.getRequiresExpr().getEnclosingStmt().getParent()
)
)
exists(Parameter p | this = p and result = p.getFunction())
or
exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n)
or
exists(TemplateVariable tv | this = tv.getATemplateArgument() and result = tv)
or
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
or
// result instanceof Block|Function
// result instanceof block|function
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
or
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
or
// result instanceof Stmt
// result instanceof stmt
exists(ControlStructure s | this = s and result = s.getParent())
or
using_container(unresolveElement(result), underlyingElement(this))

View File

@@ -230,14 +230,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
)
}
/**
* Gets a non-implicit function declaration entry.
*/
FunctionDeclarationEntry getAnExplicitDeclarationEntry() {
result = this.getADeclarationEntry() and
not result.isImplicit()
}
private predicate declEntry(FunctionDeclarationEntry fde) {
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
// If one .cpp file specializes a function, and another calls the

View File

@@ -7,8 +7,8 @@ import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ function parameter, catch block parameter, or requires expression parameter.
* For example the function parameter `p` and the catch block parameter `e` in the following
* A C/C++ function parameter or catch block parameter. For example the
* function parameter `p` and the catch block parameter `e` in the following
* code:
* ```
* void myFunction(int p) {
@@ -20,8 +20,8 @@ private import semmle.code.cpp.internal.ResolveClass
* }
* ```
*
* For catch block parameters and expression , there is a one-to-one
* correspondence between the `Parameter` and its `VariableDeclarationEntry`.
* For catch block parameters, there is a one-to-one correspondence between
* the `Parameter` and its `ParameterDeclarationEntry`.
*
* For function parameters, there is a one-to-many relationship between
* `Parameter` and `ParameterDeclarationEntry`, because one function can
@@ -73,8 +73,7 @@ class Parameter extends LocalScopeVariable, @parameter {
}
private VariableDeclarationEntry getANamedDeclarationEntry() {
result = this.getAnEffectiveDeclarationEntry() and
exists(string name | var_decls(unresolveElement(result), _, _, name, _) | name != "")
result = this.getAnEffectiveDeclarationEntry() and result.getName() != ""
}
/**
@@ -119,12 +118,6 @@ class Parameter extends LocalScopeVariable, @parameter {
*/
BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the requires expression to which the parameter belongs, if it is a
* requires expression parameter.
*/
RequiresExpr getRequiresExpr() { params(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the zero-based index of this parameter.
*

View File

@@ -80,10 +80,6 @@ private Declaration getAnEnclosingDeclaration(Locatable ast) {
or
result = ast.(Parameter).getFunction()
or
result = ast.(Parameter).getCatchBlock().getEnclosingFunction()
or
result = ast.(Parameter).getRequiresExpr().getEnclosingFunction()
or
result = ast.(Expr).getEnclosingDeclaration()
or
result = ast.(Initializer).getDeclaration()
@@ -103,10 +99,7 @@ private newtype TPrintAstNode =
stmt.getADeclarationEntry() = entry and
shouldPrintDeclaration(stmt.getEnclosingFunction())
} or
TFunctionParametersNode(Function func) { shouldPrintDeclaration(func) } or
TRequiresExprParametersNode(RequiresExpr req) {
shouldPrintDeclaration(getAnEnclosingDeclaration(req))
} or
TParametersNode(Function func) { shouldPrintDeclaration(func) } or
TConstructorInitializersNode(Constructor ctor) {
ctor.hasEntryPoint() and
shouldPrintDeclaration(ctor)
@@ -310,14 +303,14 @@ class ExprNode extends AstNode {
ExprNode() { expr = ast }
override PrintAstNode getChildInternal(int childIndex) {
result.(AstNode).getAst() = expr.getChild(childIndex)
override AstNode getChildInternal(int childIndex) {
result.getAst() = expr.getChild(childIndex)
or
childIndex = max(int index | exists(expr.getChild(index)) or index = 0) + 1 and
result.(AstNode).getAst() = expr.(ConditionDeclExpr).getInitializingExpr()
result.getAst() = expr.(ConditionDeclExpr).getInitializingExpr()
or
exists(int destructorIndex |
result.(AstNode).getAst() = expr.getImplicitDestructorCall(destructorIndex) and
result.getAst() = expr.getImplicitDestructorCall(destructorIndex) and
childIndex = destructorIndex + max(int index | exists(expr.getChild(index)) or index = 0) + 2
)
}
@@ -336,8 +329,7 @@ class ExprNode extends AstNode {
}
override string getChildAccessorPredicateInternal(int childIndex) {
result =
getChildAccessorWithoutConversions(ast, this.getChildInternal(childIndex).(AstNode).getAst())
result = getChildAccessorWithoutConversions(ast, this.getChildInternal(childIndex).getAst())
}
/**
@@ -417,26 +409,6 @@ class StmtExprNode extends ExprNode {
}
}
/**
* A node representing a `RequiresExpr`
*/
class RequiresExprNode extends ExprNode {
override RequiresExpr expr;
override PrintAstNode getChildInternal(int childIndex) {
result = super.getChildInternal(childIndex)
or
childIndex = -1 and
result.(RequiresExprParametersNode).getRequiresExpr() = expr
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = super.getChildAccessorPredicateInternal(childIndex)
or
childIndex = -1 and result = "<params>"
}
}
/**
* A node representing a `DeclarationEntry`.
*/
@@ -538,22 +510,6 @@ class DeclStmtNode extends StmtNode {
}
}
/**
* A node representing a `Handler`.
*/
class HandlerNode extends ChildStmtNode {
Handler handler;
HandlerNode() { handler = stmt }
override BaseAstNode getChildInternal(int childIndex) {
result = super.getChildInternal(childIndex)
or
childIndex = -1 and
result.getAst() = handler.getParameter()
}
}
/**
* A node representing a `Parameter`.
*/
@@ -596,10 +552,10 @@ class InitializerNode extends AstNode {
/**
* A node representing the parameters of a `Function`.
*/
class FunctionParametersNode extends PrintAstNode, TFunctionParametersNode {
class ParametersNode extends PrintAstNode, TParametersNode {
Function func;
FunctionParametersNode() { this = TFunctionParametersNode(func) }
ParametersNode() { this = TParametersNode(func) }
final override string toString() { result = "" }
@@ -620,33 +576,6 @@ class FunctionParametersNode extends PrintAstNode, TFunctionParametersNode {
final Function getFunction() { result = func }
}
/**
* A node representing the parameters of a `RequiresExpr`.
*/
class RequiresExprParametersNode extends PrintAstNode, TRequiresExprParametersNode {
RequiresExpr req;
RequiresExprParametersNode() { this = TRequiresExprParametersNode(req) }
final override string toString() { result = "" }
final override Location getLocation() { result = getRepresentativeLocation(req) }
override AstNode getChildInternal(int childIndex) {
result.getAst() = req.getParameter(childIndex)
}
override string getChildAccessorPredicateInternal(int childIndex) {
exists(this.getChildInternal(childIndex)) and
result = "getParameter(" + childIndex.toString() + ")"
}
/**
* Gets the `RequiresExpr` for which this node represents the parameters.
*/
final RequiresExpr getRequiresExpr() { result = req }
}
/**
* A node representing the initializer list of a `Constructor`.
*/
@@ -750,7 +679,7 @@ class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(FunctionParametersNode).getFunction() = func
result.(ParametersNode).getFunction() = func
or
childIndex = 1 and
result.(ConstructorInitializersNode).getConstructor() = func
@@ -825,8 +754,6 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
or
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
or
s.(Handler).getParameter() = e and pred = "getParameter()"
or
s.(IfStmt).getInitialization() = e and pred = "getInitialization()"
or
s.(IfStmt).getCondition() = e and pred = "getCondition()"
@@ -974,11 +901,6 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
or
expr.(CommaExpr).getRightOperand() = ele and pred = "getRightOperand()"
or
expr.(CompoundRequirementExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(CompoundRequirementExpr).getReturnTypeRequirement() = ele and
pred = "getReturnTypeRequirement()"
or
expr.(ConditionDeclExpr).getVariableAccess() = ele and pred = "getVariableAccess()"
or
expr.(ConstructorFieldInit).getExpr() = ele and pred = "getExpr()"
@@ -999,8 +921,6 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
or
expr.(LambdaExpression).getInitializer() = ele and pred = "getInitializer()"
or
expr.(NestedRequirementExpr).getConstraint() = ele and pred = "getConstraint()"
or
expr.(NewOrNewArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or
expr.(NewOrNewArrayExpr).getAlignmentArgument() = ele and pred = "getAlignmentArgument()"
@@ -1040,11 +960,6 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
or
expr.(UnaryOperation).getOperand() = ele and pred = "getOperand()"
or
exists(int n |
expr.(RequiresExpr).getRequirement(n) = ele and
pred = "getRequirement(" + n + ")"
)
or
expr.(SizeofExprOperator).getExprOperand() = ele and pred = "getExprOperand()"
or
expr.(StmtExpr).getStmt() = ele and pred = "getStmt()"

View File

@@ -241,10 +241,6 @@ class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
name != "" and result = name
or
name = "" and result = this.getVariable().(LocalVariable).getName()
or
name = "" and
not this instanceof ParameterDeclarationEntry and
result = this.getVariable().(Parameter).getName()
)
)
}
@@ -299,11 +295,19 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
private string getAnonymousParameterDescription() {
not exists(this.getName()) and
exists(string anon |
anon = "(unnamed parameter " + this.getIndex().toString() + ")" and
exists(string idx |
idx =
((this.getIndex() + 1).toString() + "th")
.replaceAll("1th", "1st")
.replaceAll("2th", "2nd")
.replaceAll("3th", "3rd")
.replaceAll("11st", "11th")
.replaceAll("12nd", "12th")
.replaceAll("13rd", "13th") and
if exists(this.getCanonicalName())
then result = "declaration of " + this.getCanonicalName() + " as " + anon
else result = "declaration of " + anon
then
result = "declaration of " + this.getCanonicalName() + " as anonymous " + idx + " parameter"
else result = "declaration of " + idx + " parameter"
)
}

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -281,9 +281,3 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) {
}
private predicate iteratorDereference(Call c) { c.getTarget() instanceof IteratorReferenceFunction }
/**
* Holds if the additional step from `src` to `sink` should be considered in
* speculative taint flow exploration.
*/
predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { none() }

View File

@@ -149,11 +149,6 @@ class Call extends Expr, NameQualifiableElement, TCall {
variableAddressEscapesTreeNonConst(va, this.getQualifier().getFullyConverted()) and
i = -1
}
/** Holds if this expression could be the return value of an implicitly declared function. */
predicate mayBeFromImplicitlyDeclaredFunction() {
this.getTarget().getADeclarationEntry().isImplicit()
}
}
/**

View File

@@ -181,7 +181,12 @@ class VariableDeclarationEntry extends @var_decl {
string getName() { var_decls(this, _, _, result, _) and result != "" }
}
class Parameter extends LocalScopeVariable, @parameter { }
class Parameter extends LocalScopeVariable, @parameter {
@functionorblock function;
int index;
Parameter() { params(this, function, index, _) }
}
class GlobalOrNamespaceVariable extends Variable, @globalvariable { }

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -261,17 +261,13 @@ deprecated private module Config implements FullStateConfigSig {
model = ""
}
predicate isAdditionalFlowStep(
Node node1, FlowState state1, Node node2, FlowState state2, string model
) {
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1) and
model = ""
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1 and
model = ""
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {

View File

@@ -1328,10 +1328,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
call.(SummaryCall).getReceiver() = receiver.(FlowSummaryNode).getSummaryNode() or
call.asCallInstruction().getCallTargetOperand() = receiver.asOperand()
) and
call.(SummaryCall).getReceiver() = receiver.(FlowSummaryNode).getSummaryNode() and
exists(kind)
}

View File

@@ -17,7 +17,6 @@ private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
private import Node0ToString
private import DataFlowDispatch as DataFlowDispatch
import ExprNodes
/**
@@ -2498,16 +2497,3 @@ class AdditionalCallTarget extends Unit {
*/
abstract Declaration viableTarget(Call call);
}
/**
* Gets a function that may be called by `call`.
*
* Note that `call` may be a call to a function pointer expression.
*/
Function getARuntimeTarget(Call call) {
exists(DataFlowCall dfCall | dfCall.asCallInstruction().getUnconvertedResultExpression() = call |
result = DataFlowDispatch::viableCallable(dfCall).asSourceCallable()
or
result = DataFlowImplCommon::viableCallableLambda(dfCall, _).asSourceCallable()
)
}

View File

@@ -212,30 +212,3 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
nodeOut = callOutput(call, modelOut)
)
}
import SpeculativeTaintFlow
private module SpeculativeTaintFlow {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate
/**
* Holds if the additional step from `src` to `sink` should be considered in
* speculative taint flow exploration.
*/
predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) {
exists(DataFlowCall call, ArgumentPosition argpos |
// TODO: exclude neutrals and anything that has QL modeling.
not exists(DataFlowDispatch::viableCallable(call)) and
src.(DataFlowPrivate::ArgumentNode).argumentOf(call, argpos)
|
not argpos.(DirectPosition).getIndex() = -1 and
sink.(PostUpdateNode)
.getPreUpdateNode()
.(DataFlowPrivate::ArgumentNode)
.argumentOf(call, any(DirectPosition qualpos | qualpos.getIndex() = -1))
or
sink.(DataFlowPrivate::OutNode).getCall() = call
)
}
}

View File

@@ -6,112 +6,6 @@
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
* to dump.
*
* Anatomy of a printed IR instruction
*
* An instruction:
*
* ```
* # 2281| v2281_19(void) = Call[~String] : func:r2281_18, this:r2281_17
* ```
*
* The prefix `# 2281|` specifies that this instruction was generated by the C++ source code on line 2281.
* Scrolling up in the printed output, one will eventually find the name of the file to which the line
* belongs.
*
* `v2281_19(void)` is the result of the instruction. Here, `v` means this is a void result or operand (so
* there should be no later uses of the result; see below for other possible values). The `2281_19` is a
* unique ID for the result. This is usually just the line number plus a small integer suffix to make it
* unique within the function. The type of the result is `void`. In this case, it is `void`, because
* `~String` returns `void`. The type of the result is usually just the name of the appropriate C++ type,
* but it will sometimes be a type like `glval<int>`, which means result holds a glvalue, which at the
* IR level works like a pointer. In other words, in the source code the type was `int`, but it is really
* more like an `int*`. We see this, for example, in `x = y;`, where `x` is a glvalue.
*
* `Call` is the opcode of the instruction. Common opcodes include:
*
* * Arithmetic operations: `Add`, `Sub`, `Mul`, etc.
* * Memory access operations: `Load`, `Store`.
* * Function calls: `Call`.
* * Literals: `Constant`.
* * Variable addresses: `VariableAddress`.
* * Function entry points: `EnterFunction`.
* * Return from a function: `Return`, `ReturnVoid`. Note that the value being returned is set separately by a
* `Store` to a special `#return` variable.
* * Stack unwinding for C++ function that throw and where the exception escapes the function: `Unwind`.
* * Common exit point for `Unwind` and `Return`: `ExitFunction`.
* * SSA-related opcodes: `Phi`, `Chi`.
*
* `[~String]` denotes additional information. The information might be present earlier in the IR, as is the case
* for `Call`, where it is the name of the called function. This is also the case for `Load` and `Store`, where it
* is the name of the variable that loaded or stored (if known). In the case of `Constant`, `FieldAddress`, and
* `VariableAddress`, the information between brackets does not occur earlier.
*
* `func:r2281_18` and `this:r28281_17` are the operands of the instruction. The `func:` prefix denotes the operand
* that holds the address of the called function. The `this:` prefix denotes the argument to the special `this`
* parameter of an instance member function. `r2281_18`, `r2281_17` are the unique IDs of the operands. Each of these
* matches the ID of a previously seen result, showing where that value came from. The `r` means that these are
* "register" operands (see below).
*
* Result and operand kinds:
*
* Every result and operand is one of these three kinds:
*
* * `r` "register". These operands are not stored in any particular memory location. We can think of them as
* temporary values created during the evaluation of an expression. A register operand almost always has one
* use, often in the same block as its definition.
* * `m` "memory". These operands represents accesses to a specific memory location. The location could be a
* local variable, a global variable, a field of an object, an element of an array, or any memory that we happen
* to have a pointer to. These only occur as the result of a `Store`, the source operand of a `Load` or on the
* SSA instructions (`Phi`, `Chi`).
* * `v` "void". Really just a register operand, but we mark register operands of type void with this special prefix
* so we know that there is no actual value there.
*
* Branches in the IR:
*
* The IR is divided into basic blocks. At the end of each block, there are one or more edges showing the possible
* control flow successors of the block.
*
* ```
* # 44| v44_3(void) = ConditionalBranch : r44_2
* #-----| False -> Block 4
* #-----| True -> Block 3
* ```
* Here we have a block that ends with a conditional branch. The two edges show where the control flows to depending
* on whether the condition is true or false.
*
* SSA instructions:
*
* We use `Phi` instructions in SSA to create a single definition for a variable that might be assigned on multiple
* control flow paths. The `Phi` instruction merges the potential values of that variable from each predecessor edge,
* and the resulting definition is then used wherever that variable is accessed later on.
*
* When dealing with aliased memory, we use the `Chi` instruction to create a single definition for memory that might
* or might not have been updated by a store, depending on the actual address that was written to. For example, take:
*
* ```cpp
* int x = 5;
* int y = 7;
* int* p = condition ? &x : &y;
* *p = 6;
* return x;
* ```
*
* At the point where we store to `*p`, we do not know whether `p` points to `x` or `y`. Thus, we do not know whether
* `return x;` is going to return the value that `x` was originally initialized to (5), or whether it will return 6,
* because it was overwritten by `*p = 6;`. We insert a `Chi` instruction immediately after the store to `*p`:
*
* ```
* r2(int) = Constant[6]
* r3(int*) = <<value of p>>
* m4(int) = Store : &r3, r2 // Stores the constant 6 to *p
* m5(unknown) = Chi : total:m1, partial:m4
* ```
* The `partial:` operand represents the memory that was just stored. The `total:` operand represents the previous
* contents of all of the memory that `p` might have pointed to (in this case, both `x` and `y`). The result of the
* `Chi` represents the new contents of whatever memory the `total:` operand referred to. We usually do not know exactly
* which parts of that memory were overwritten, but it does model that any of that memory could have been modified, so
* that later instructions do not assume that the memory was unchanged.
*/
private import internal.IRInternal

View File

@@ -683,13 +683,8 @@ private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
def.getVirtualVariable() = use.getVirtualVariable() and
def instanceof EntireAllocationMemoryLocation and
(
// EntireAllocationMemoryLocation exactly overlaps any EntireAllocationMemoryLocation for the
// same allocation. Checking the allocation, rather than the memory location itself, ensures
// that we get the right relationship between the "must" and "may" memory locations for that
// allocation.
// Note that if one of the locations is a "may" access, the overlap will be downgraded to
// `MustTotallyOverlap` or `MayPartialOverlap` in `getOverlap()`.
use.(EntireAllocationMemoryLocation).getAnAllocation() = def.getAnAllocation() and
// EntireAllocationMemoryLocation exactly overlaps itself.
use instanceof EntireAllocationMemoryLocation and
result instanceof MustExactlyOverlap
or
not use instanceof EntireAllocationMemoryLocation and

View File

@@ -6,112 +6,6 @@
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
* to dump.
*
* Anatomy of a printed IR instruction
*
* An instruction:
*
* ```
* # 2281| v2281_19(void) = Call[~String] : func:r2281_18, this:r2281_17
* ```
*
* The prefix `# 2281|` specifies that this instruction was generated by the C++ source code on line 2281.
* Scrolling up in the printed output, one will eventually find the name of the file to which the line
* belongs.
*
* `v2281_19(void)` is the result of the instruction. Here, `v` means this is a void result or operand (so
* there should be no later uses of the result; see below for other possible values). The `2281_19` is a
* unique ID for the result. This is usually just the line number plus a small integer suffix to make it
* unique within the function. The type of the result is `void`. In this case, it is `void`, because
* `~String` returns `void`. The type of the result is usually just the name of the appropriate C++ type,
* but it will sometimes be a type like `glval<int>`, which means result holds a glvalue, which at the
* IR level works like a pointer. In other words, in the source code the type was `int`, but it is really
* more like an `int*`. We see this, for example, in `x = y;`, where `x` is a glvalue.
*
* `Call` is the opcode of the instruction. Common opcodes include:
*
* * Arithmetic operations: `Add`, `Sub`, `Mul`, etc.
* * Memory access operations: `Load`, `Store`.
* * Function calls: `Call`.
* * Literals: `Constant`.
* * Variable addresses: `VariableAddress`.
* * Function entry points: `EnterFunction`.
* * Return from a function: `Return`, `ReturnVoid`. Note that the value being returned is set separately by a
* `Store` to a special `#return` variable.
* * Stack unwinding for C++ function that throw and where the exception escapes the function: `Unwind`.
* * Common exit point for `Unwind` and `Return`: `ExitFunction`.
* * SSA-related opcodes: `Phi`, `Chi`.
*
* `[~String]` denotes additional information. The information might be present earlier in the IR, as is the case
* for `Call`, where it is the name of the called function. This is also the case for `Load` and `Store`, where it
* is the name of the variable that loaded or stored (if known). In the case of `Constant`, `FieldAddress`, and
* `VariableAddress`, the information between brackets does not occur earlier.
*
* `func:r2281_18` and `this:r28281_17` are the operands of the instruction. The `func:` prefix denotes the operand
* that holds the address of the called function. The `this:` prefix denotes the argument to the special `this`
* parameter of an instance member function. `r2281_18`, `r2281_17` are the unique IDs of the operands. Each of these
* matches the ID of a previously seen result, showing where that value came from. The `r` means that these are
* "register" operands (see below).
*
* Result and operand kinds:
*
* Every result and operand is one of these three kinds:
*
* * `r` "register". These operands are not stored in any particular memory location. We can think of them as
* temporary values created during the evaluation of an expression. A register operand almost always has one
* use, often in the same block as its definition.
* * `m` "memory". These operands represents accesses to a specific memory location. The location could be a
* local variable, a global variable, a field of an object, an element of an array, or any memory that we happen
* to have a pointer to. These only occur as the result of a `Store`, the source operand of a `Load` or on the
* SSA instructions (`Phi`, `Chi`).
* * `v` "void". Really just a register operand, but we mark register operands of type void with this special prefix
* so we know that there is no actual value there.
*
* Branches in the IR:
*
* The IR is divided into basic blocks. At the end of each block, there are one or more edges showing the possible
* control flow successors of the block.
*
* ```
* # 44| v44_3(void) = ConditionalBranch : r44_2
* #-----| False -> Block 4
* #-----| True -> Block 3
* ```
* Here we have a block that ends with a conditional branch. The two edges show where the control flows to depending
* on whether the condition is true or false.
*
* SSA instructions:
*
* We use `Phi` instructions in SSA to create a single definition for a variable that might be assigned on multiple
* control flow paths. The `Phi` instruction merges the potential values of that variable from each predecessor edge,
* and the resulting definition is then used wherever that variable is accessed later on.
*
* When dealing with aliased memory, we use the `Chi` instruction to create a single definition for memory that might
* or might not have been updated by a store, depending on the actual address that was written to. For example, take:
*
* ```cpp
* int x = 5;
* int y = 7;
* int* p = condition ? &x : &y;
* *p = 6;
* return x;
* ```
*
* At the point where we store to `*p`, we do not know whether `p` points to `x` or `y`. Thus, we do not know whether
* `return x;` is going to return the value that `x` was originally initialized to (5), or whether it will return 6,
* because it was overwritten by `*p = 6;`. We insert a `Chi` instruction immediately after the store to `*p`:
*
* ```
* r2(int) = Constant[6]
* r3(int*) = <<value of p>>
* m4(int) = Store : &r3, r2 // Stores the constant 6 to *p
* m5(unknown) = Chi : total:m1, partial:m4
* ```
* The `partial:` operand represents the memory that was just stored. The `total:` operand represents the previous
* contents of all of the memory that `p` might have pointed to (in this case, both `x` and `y`). The result of the
* `Chi` represents the new contents of whatever memory the `total:` operand referred to. We usually do not know exactly
* which parts of that memory were overwritten, but it does model that any of that memory could have been modified, so
* that later instructions do not assume that the memory was unchanged.
*/
private import internal.IRInternal

View File

@@ -196,8 +196,6 @@ private predicate isInvalidFunction(Function func) {
expr.getEnclosingFunction() = func and
not exists(expr.getType())
)
or
count(func.getEntryPoint().getLocation()) > 1
}
/**

View File

@@ -6,112 +6,6 @@
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
* to dump.
*
* Anatomy of a printed IR instruction
*
* An instruction:
*
* ```
* # 2281| v2281_19(void) = Call[~String] : func:r2281_18, this:r2281_17
* ```
*
* The prefix `# 2281|` specifies that this instruction was generated by the C++ source code on line 2281.
* Scrolling up in the printed output, one will eventually find the name of the file to which the line
* belongs.
*
* `v2281_19(void)` is the result of the instruction. Here, `v` means this is a void result or operand (so
* there should be no later uses of the result; see below for other possible values). The `2281_19` is a
* unique ID for the result. This is usually just the line number plus a small integer suffix to make it
* unique within the function. The type of the result is `void`. In this case, it is `void`, because
* `~String` returns `void`. The type of the result is usually just the name of the appropriate C++ type,
* but it will sometimes be a type like `glval<int>`, which means result holds a glvalue, which at the
* IR level works like a pointer. In other words, in the source code the type was `int`, but it is really
* more like an `int*`. We see this, for example, in `x = y;`, where `x` is a glvalue.
*
* `Call` is the opcode of the instruction. Common opcodes include:
*
* * Arithmetic operations: `Add`, `Sub`, `Mul`, etc.
* * Memory access operations: `Load`, `Store`.
* * Function calls: `Call`.
* * Literals: `Constant`.
* * Variable addresses: `VariableAddress`.
* * Function entry points: `EnterFunction`.
* * Return from a function: `Return`, `ReturnVoid`. Note that the value being returned is set separately by a
* `Store` to a special `#return` variable.
* * Stack unwinding for C++ function that throw and where the exception escapes the function: `Unwind`.
* * Common exit point for `Unwind` and `Return`: `ExitFunction`.
* * SSA-related opcodes: `Phi`, `Chi`.
*
* `[~String]` denotes additional information. The information might be present earlier in the IR, as is the case
* for `Call`, where it is the name of the called function. This is also the case for `Load` and `Store`, where it
* is the name of the variable that loaded or stored (if known). In the case of `Constant`, `FieldAddress`, and
* `VariableAddress`, the information between brackets does not occur earlier.
*
* `func:r2281_18` and `this:r28281_17` are the operands of the instruction. The `func:` prefix denotes the operand
* that holds the address of the called function. The `this:` prefix denotes the argument to the special `this`
* parameter of an instance member function. `r2281_18`, `r2281_17` are the unique IDs of the operands. Each of these
* matches the ID of a previously seen result, showing where that value came from. The `r` means that these are
* "register" operands (see below).
*
* Result and operand kinds:
*
* Every result and operand is one of these three kinds:
*
* * `r` "register". These operands are not stored in any particular memory location. We can think of them as
* temporary values created during the evaluation of an expression. A register operand almost always has one
* use, often in the same block as its definition.
* * `m` "memory". These operands represents accesses to a specific memory location. The location could be a
* local variable, a global variable, a field of an object, an element of an array, or any memory that we happen
* to have a pointer to. These only occur as the result of a `Store`, the source operand of a `Load` or on the
* SSA instructions (`Phi`, `Chi`).
* * `v` "void". Really just a register operand, but we mark register operands of type void with this special prefix
* so we know that there is no actual value there.
*
* Branches in the IR:
*
* The IR is divided into basic blocks. At the end of each block, there are one or more edges showing the possible
* control flow successors of the block.
*
* ```
* # 44| v44_3(void) = ConditionalBranch : r44_2
* #-----| False -> Block 4
* #-----| True -> Block 3
* ```
* Here we have a block that ends with a conditional branch. The two edges show where the control flows to depending
* on whether the condition is true or false.
*
* SSA instructions:
*
* We use `Phi` instructions in SSA to create a single definition for a variable that might be assigned on multiple
* control flow paths. The `Phi` instruction merges the potential values of that variable from each predecessor edge,
* and the resulting definition is then used wherever that variable is accessed later on.
*
* When dealing with aliased memory, we use the `Chi` instruction to create a single definition for memory that might
* or might not have been updated by a store, depending on the actual address that was written to. For example, take:
*
* ```cpp
* int x = 5;
* int y = 7;
* int* p = condition ? &x : &y;
* *p = 6;
* return x;
* ```
*
* At the point where we store to `*p`, we do not know whether `p` points to `x` or `y`. Thus, we do not know whether
* `return x;` is going to return the value that `x` was originally initialized to (5), or whether it will return 6,
* because it was overwritten by `*p = 6;`. We insert a `Chi` instruction immediately after the store to `*p`:
*
* ```
* r2(int) = Constant[6]
* r3(int*) = <<value of p>>
* m4(int) = Store : &r3, r2 // Stores the constant 6 to *p
* m5(unknown) = Chi : total:m1, partial:m4
* ```
* The `partial:` operand represents the memory that was just stored. The `total:` operand represents the previous
* contents of all of the memory that `p` might have pointed to (in this case, both `x` and `y`). The result of the
* `Chi` represents the new contents of whatever memory the `total:` operand referred to. We usually do not know exactly
* which parts of that memory were overwritten, but it does model that any of that memory could have been modified, so
* that later instructions do not assume that the memory was unchanged.
*/
private import internal.IRInternal

View File

@@ -7,7 +7,7 @@ import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/** The function `fopen` and friends. */
private class Fopen extends Function, AliasFunction, SideEffectFunction, TaintFunction {
private class Fopen extends Function, AliasFunction, SideEffectFunction {
Fopen() {
this.hasGlobalOrStdName(["fopen", "fopen_s", "freopen"])
or
@@ -47,22 +47,4 @@ private class Fopen extends Function, AliasFunction, SideEffectFunction, TaintFu
i = 0 and
buffer = true
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
this.hasGlobalOrStdName(["fopen", "freopen"]) or
this.hasGlobalName(["_wfopen", "_fsopen", "_wfsopen"])
) and
input.isParameterDeref(0) and
output.isReturnValueDeref()
or
// The out parameter is a pointer to a `FILE*`.
this.hasGlobalOrStdName("fopen_s") and
input.isParameterDeref(1) and
output.isParameterDeref(0, 2)
or
this.hasGlobalName(["_open", "_wopen"]) and
input.isParameterDeref(0) and
output.isReturnValue()
}
}

View File

@@ -91,7 +91,7 @@ private class Sprintf extends FormattingFunction, NonThrowingFunction {
override int getFirstFormatArgumentIndex() {
if this.hasName("__builtin___sprintf_chk")
then result = 4
else result = super.getFirstFormatArgumentIndex()
else result = this.getNumberOfParameters()
}
}

View File

@@ -42,21 +42,6 @@ private Type getAFormatterWideTypeOrDefault() {
* A standard library function that uses a `printf`-like formatting string.
*/
abstract class FormattingFunction extends ArrayFunction, TaintFunction {
int firstFormatArgumentIndex;
FormattingFunction() {
firstFormatArgumentIndex > 0 and
if this.hasDefinition()
then firstFormatArgumentIndex = this.getDefinition().getNumberOfParameters()
else
if this instanceof BuiltInFunction
then firstFormatArgumentIndex = this.getNumberOfParameters()
else
forex(FunctionDeclarationEntry fde | fde = this.getAnExplicitDeclarationEntry() |
firstFormatArgumentIndex = fde.getNumberOfParameters()
)
}
/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();
@@ -136,7 +121,33 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
* the first format specifier in the format string. We ignore all
* implicit function definitions.
*/
int getFirstFormatArgumentIndex() { result = firstFormatArgumentIndex }
int getFirstFormatArgumentIndex() {
// The formatting function either has a definition in the snapshot, or all
// `DeclarationEntry`s agree on the number of parameters (otherwise we don't
// really know the correct number)
if this.hasDefinition()
then result = this.getDefinition().getNumberOfParameters()
else result = this.getNumberOfExplicitParameters()
}
/**
* Gets a non-implicit function declaration entry.
*/
private FunctionDeclarationEntry getAnExplicitDeclarationEntry() {
result = this.getADeclarationEntry() and
not result.isImplicit()
}
/**
* Gets the number of parameters, excluding any parameters that have been defined
* from implicit function declarations. If there is some inconsistency in the number
* of parameters, then don't return anything.
*/
private int getNumberOfExplicitParameters() {
forex(FunctionDeclarationEntry fde | fde = this.getAnExplicitDeclarationEntry() |
result = fde.getNumberOfParameters()
)
}
/**
* Gets the position of the buffer size argument, if any.

View File

@@ -192,37 +192,6 @@ private class UnsignedMulExpr extends MulExpr {
}
}
/**
* Gets the value of the `EOF` macro.
*
* This is typically `"-1"`, but this is not guaranteed to be the case on all
* systems.
*/
private int getEofValue() {
exists(MacroInvocation mi |
mi.getMacroName() = "EOF" and
result = unique( | | mi.getExpr().getValue().toInt())
)
}
/** Get standard `getc` function or related variants. */
private class Getc extends Function {
Getc() { this.hasGlobalOrStdOrBslName(["fgetc", "getc"]) }
}
/** A call to `getc` */
private class CallToGetc extends FunctionCall {
CallToGetc() { this.getTarget() instanceof Getc }
}
/**
* A call to `getc` that we can analyze because we know
* the value of the `EOF` macro.
*/
private class AnalyzableCallToGetc extends CallToGetc {
AnalyzableCallToGetc() { exists(getEofValue()) }
}
/**
* Holds if `expr` is effectively a multiplication of `operand` with the
* positive constant `positive`.
@@ -318,8 +287,6 @@ private predicate analyzableExpr(Expr e) {
or
e instanceof RemExpr
or
e instanceof AnalyzableCallToGetc
or
// A conversion is analyzable, provided that its child has an arithmetic
// type. (Sometimes the child is a reference type, and so does not get
// any bounds.) Rather than checking whether the type of the child is
@@ -894,14 +861,6 @@ private float getLowerBoundsImpl(Expr expr) {
)
)
or
exists(AnalyzableCallToGetc getc |
expr = getc and
// from https://en.cppreference.com/w/c/io/fgetc:
// On success, returns the obtained character as an unsigned char
// converted to an int. On failure, returns EOF.
result = min([typeLowerBound(any(UnsignedCharType pct)), getEofValue()])
)
or
// If the conversion is to an arithmetic type then we just return the
// lower bound of the child. We do not need to handle truncation and
// overflow here, because that is done in `getTruncatedLowerBounds`.
@@ -1096,14 +1055,6 @@ private float getUpperBoundsImpl(Expr expr) {
)
)
or
exists(AnalyzableCallToGetc getc |
expr = getc and
// from https://en.cppreference.com/w/c/io/fgetc:
// On success, returns the obtained character as an unsigned char
// converted to an int. On failure, returns EOF.
result = max([typeUpperBound(any(UnsignedCharType pct)), getEofValue()])
)
or
// If the conversion is to an arithmetic type then we just return the
// upper bound of the child. We do not need to handle truncation and
// overflow here, because that is done in `getTruncatedUpperBounds`.

View File

@@ -534,7 +534,7 @@ static_asserts(
#keyset[function, index, type_id]
params(
int id: @parameter,
int function: @parameterized_element ref,
int function: @functionorblock ref,
int index: int ref,
int type_id: @type ref
);
@@ -1790,10 +1790,6 @@ case @expr.kind of
| 387 = @istriviallyrelocatable
| 388 = @datasizeof
| 389 = @c11_generic
| 390 = @requires_expr
| 391 = @nested_requirement
| 392 = @compound_requirement
| 393 = @concept_id
;
@var_args_expr = @vastartexpr
@@ -1912,10 +1908,6 @@ case @expr.kind of
| @istriviallyrelocatable
;
compound_requirement_is_noexcept(
int expr: @compound_requirement ref
);
new_allocated_type(
unique int expr: @new_expr ref,
int type_id: @type ref
@@ -2175,11 +2167,11 @@ stmt_decl_entry_bind(
int decl_entry: @element ref
);
@parameterized_element = @function | @stmt_block | @requires_expr;
@functionorblock = @function | @stmt_block;
blockscope(
unique int block: @stmt_block ref,
int enclosing: @parameterized_element ref
int enclosing: @functionorblock ref
);
@jump = @stmt_goto | @stmt_break | @stmt_continue;

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
description: Support C++20 requires expressions
compatibility: backwards

View File

@@ -1,2 +0,0 @@
description: Add requires expressions
compatibility: full

View File

@@ -1,18 +1,3 @@
## 1.2.6
### Minor Analysis Improvements
* Remove results from the `cpp/wrong-type-format-argument` ("Wrong type of arguments to formatting function") query if the argument is the return value of an implicitly declared function.
## 1.2.5
### Minor Analysis Improvements
* The `cpp/unclear-array-index-validation` ("Unclear validation of array index") query has been improved to reduce false positives and increase true positives.
* Fixed false positives in the `cpp/uninitialized-local` ("Potentially uninitialized local variable") query if there are extraction errors in the function.
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to detect byte arrays.
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to recognize dynamic checks prior to possible dangerous widening.
## 1.2.4
### Minor Analysis Improvements

View File

@@ -170,8 +170,7 @@ where
) and
not arg.isAffectedByMacro() and
not arg.isFromUninstantiatedTemplate(_) and
not actual.getUnspecifiedType() instanceof ErroneousType and
not arg.(Call).mayBeFromImplicitlyDeclaredFunction()
not actual.getUnspecifiedType() instanceof ErroneousType
select arg,
"This format specifier for type '" + expected.getName() + "' does not match the argument type '" +
actual.getUnspecifiedType().getName() + "'."

View File

@@ -14,56 +14,102 @@
import cpp
import semmle.code.cpp.controlflow.IRGuards
import semmle.code.cpp.security.FlowSources as FS
import semmle.code.cpp.dataflow.new.TaintTracking
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import ImproperArrayIndexValidation::PathGraph
import semmle.code.cpp.security.Security
predicate isFlowSource(FS::FlowSource source, string sourceType) {
sourceType = source.getSourceType()
}
predicate guardChecks(IRGuardCondition g, Expr e, boolean branch) {
exists(Operand op | op.getDef().getConvertedResultExpression() = e |
// `op < k` is true and `k > 0`
g.comparesLt(op, any(int k | k > 0), true, any(BooleanValue bv | bv.getValue() = branch))
or
// `op < _ + k` is true and `k > 0`.
g.comparesLt(op, _, any(int k | k > 0), true, branch)
or
// op == k
g.comparesEq(op, _, true, any(BooleanValue bv | bv.getValue() = branch))
or
// op == _ + k
g.comparesEq(op, _, _, true, branch)
predicate hasUpperBound(VariableAccess offsetExpr) {
exists(BasicBlock controlled, StackVariable offsetVar, SsaDefinition def |
controlled.contains(offsetExpr) and
linearBoundControls(controlled, def, offsetVar) and
offsetExpr = def.getAUse(offsetVar)
)
}
/**
* Holds if `arrayExpr` accesses an `ArrayType` with a constant size `N`, and
* the value of `offsetExpr` is known to be smaller than `N`.
*/
predicate offsetIsAlwaysInBounds(ArrayExpr arrayExpr, VariableAccess offsetExpr) {
exists(ArrayType arrayType |
arrayType = arrayExpr.getArrayBase().getUnspecifiedType() and
arrayType.getArraySize() > upperBound(offsetExpr.getFullyConverted())
pragma[noinline]
predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, StackVariable offsetVar) {
exists(GuardCondition guard, boolean branch |
guard.controls(controlled, branch) and
cmpWithLinearBound(guard, def.getAUse(offsetVar), Lesser(), branch)
)
}
predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
}
predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
predicate nodeIsBarrierEqualityCandidate(DataFlow::Node node, Operand access, Variable checkedVar) {
readsVariable(node.asInstruction(), checkedVar) and
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
}
predicate isFlowSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
predicate predictableInstruction(Instruction instr) {
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
// This could be a conversion on a string literal
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
module ImproperArrayIndexValidationConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isFlowSource(source, _) }
predicate isBarrier(DataFlow::Node node) {
node = DataFlow::BarrierGuard<guardChecks/3>::getABarrierNode()
hasUpperBound(node.asExpr())
or
// These barriers are ported from `DefaultTaintTracking` because this query is quite noisy
// otherwise.
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
or
exists(Variable checkedVar, Operand access |
readsVariable(access.getDef(), checkedVar) and
nodeIsBarrierEqualityCandidate(node, access, checkedVar)
)
or
// Don't use dataflow into binary instructions if both operands are unpredictable
exists(BinaryInstruction iTo |
iTo = node.asInstruction() and
not predictableInstruction(iTo.getLeft()) and
not predictableInstruction(iTo.getRight()) and
// propagate taint from either the pointer or the offset, regardless of predictability
not iTo instanceof PointerArithmeticInstruction
)
or
// don't use dataflow through calls to pure functions if two or more operands
// are unpredictable
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
iTo = node.asInstruction() and
isPureFunction(iTo.getStaticCallTarget().getName()) and
iFrom1 = iTo.getAnArgument() and
iFrom2 = iTo.getAnArgument() and
not predictableInstruction(iFrom1) and
not predictableInstruction(iFrom2) and
iFrom1 != iFrom2
)
}
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
predicate isSink(DataFlow::Node sink) {
exists(ArrayExpr arrayExpr, VariableAccess offsetExpr |
offsetExpr = arrayExpr.getArrayOffset() and
sink.asExpr() = offsetExpr and
not offsetIsAlwaysInBounds(arrayExpr, offsetExpr)
not hasUpperBound(offsetExpr)
)
}
}

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to detect byte arrays.
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to recognize dynamic checks prior to possible dangerous widening.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Fixed false positives in the `cpp/uninitialized-local` ("Potentially uninitialized local variable") query if there are extraction errors in the function.

View File

@@ -1,8 +0,0 @@
## 1.2.5
### Minor Analysis Improvements
* The `cpp/unclear-array-index-validation` ("Unclear validation of array index") query has been improved to reduce false positives and increase true positives.
* Fixed false positives in the `cpp/uninitialized-local` ("Potentially uninitialized local variable") query if there are extraction errors in the function.
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to detect byte arrays.
* The `cpp/incorrect-string-type-conversion` query now produces fewer false positives caused by failure to recognize dynamic checks prior to possible dangerous widening.

View File

@@ -1,5 +0,0 @@
## 1.2.6
### Minor Analysis Improvements
* Remove results from the `cpp/wrong-type-format-argument` ("Wrong type of arguments to formatting function") query if the argument is the return value of an implicitly declared function.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.2.6
lastReleaseVersion: 1.2.4

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 1.2.6
version: 1.2.5-dev
groups:
- cpp
- queries

View File

@@ -5,5 +5,31 @@
import cpp as C
private import codeql.util.test.InlineExpectationsTest
private import internal.InlineExpectationsTestImpl
private module Impl implements InlineExpectationsTestSig {
private newtype TExpectationComment = MkExpectationComment(C::CppStyleComment c)
/**
* A class representing a line comment in the CPP style.
* Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
* include the preceding comment marker (`//`).
*/
class ExpectationComment extends TExpectationComment {
C::CppStyleComment comment;
ExpectationComment() { this = MkExpectationComment(comment) }
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
string getContents() { result = comment.getContents().suffix(2) }
/** Gets a textual representation of this element. */
string toString() { result = comment.toString() }
/** Gets the location of this comment. */
Location getLocation() { result = comment.getLocation() }
}
class Location = C::Location;
}
import Make<Impl>

View File

@@ -1,21 +0,0 @@
/**
* @kind test-postprocess
*/
private import cpp
private import codeql.util.test.InlineExpectationsTest as T
private import internal.InlineExpectationsTestImpl
import T::TestPostProcessing
import T::TestPostProcessing::Make<Impl, Input>
private module Input implements T::TestPostProcessing::InputSig<Impl> {
string getRelativeUrl(Location location) {
exists(File f, int startline, int startcolumn, int endline, int endcolumn |
location.hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and
f = location.getFile()
|
result =
f.getRelativePath() + ":" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
)
}
}

View File

@@ -1,28 +0,0 @@
import cpp as C
private import codeql.util.test.InlineExpectationsTest
module Impl implements InlineExpectationsTestSig {
private newtype TExpectationComment = MkExpectationComment(C::CppStyleComment c)
/**
* A class representing a line comment in the CPP style.
* Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
* include the preceding comment marker (`//`).
*/
class ExpectationComment extends TExpectationComment {
C::CppStyleComment comment;
ExpectationComment() { this = MkExpectationComment(comment) }
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
string getContents() { result = comment.getContents().suffix(2) }
/** Gets a textual representation of this element. */
string toString() { result = comment.toString() }
/** Gets the location of this comment. */
Location getLocation() { result = comment.getLocation() }
}
class Location = C::Location;
}

View File

@@ -870,8 +870,6 @@ Throw.cpp:
# 8| Type = [BoolType] bool
# 8| ValueCategory = prvalue
# 12| getChild(1): [Handler] <handler>
# 12| getParameter(): [Parameter] e
# 12| Type = [PointerType] E *
# 12| getBlock(): [CatchBlock] { ... }
# 13| getStmt(0): [ExprStmt] ExprStmt
# 13| getExpr(): [ReThrowExpr] re-throw exception

View File

@@ -6,7 +6,7 @@ int wprintf (const wchar_t* format, ...);
int strlen( const char * string );
int checkErrors();
static void goodTest0()
void goodTest0()
{
char * ptr = "123456789";
int ret;
@@ -17,7 +17,7 @@ static void goodTest0()
ptr += ret;
}
}
static void goodTest1(const char* ptr)
void goodTest1(const char* ptr)
{
int ret;
int len;
@@ -27,7 +27,7 @@ static void goodTest1(const char* ptr)
ptr += ret;
}
}
static void goodTest2(char* ptr)
void goodTest2(char* ptr)
{
int ret;
ptr[10]=0;
@@ -38,7 +38,7 @@ static void goodTest2(char* ptr)
}
}
static void goodTest3(const char* ptr)
void goodTest3(const char* ptr)
{
int ret;
int len;
@@ -48,7 +48,7 @@ static void goodTest3(const char* ptr)
ptr += ret;
}
}
static void goodTest4(const char* ptr)
void goodTest4(const char* ptr)
{
int ret;
int len;
@@ -58,7 +58,7 @@ static void goodTest4(const char* ptr)
ptr += ret;
}
}
static void badTest1(const char* ptr)
void badTest1(const char* ptr)
{
int ret;
int len;
@@ -68,7 +68,7 @@ static void badTest1(const char* ptr)
ptr += ret;
}
}
static void badTest2(const char* ptr)
void badTest2(const char* ptr)
{
int ret;
int len;
@@ -79,7 +79,7 @@ static void badTest2(const char* ptr)
}
}
static void goodTest5(const char* ptr,wchar_t *wc,int wc_len)
void goodTest5(const char* ptr,wchar_t *wc,int wc_len)
{
int ret;
int len;
@@ -96,7 +96,7 @@ static void goodTest5(const char* ptr,wchar_t *wc,int wc_len)
}
}
static void badTest3(const char* ptr,int wc_len)
void badTest3(const char* ptr,int wc_len)
{
int ret;
int len;
@@ -113,7 +113,7 @@ static void badTest3(const char* ptr,int wc_len)
wc++;
}
}
static void badTest4(const char* ptr,int wc_len)
void badTest4(const char* ptr,int wc_len)
{
int ret;
int len;
@@ -130,7 +130,7 @@ static void badTest4(const char* ptr,int wc_len)
wc++;
}
}
static void badTest5(const char* ptr,int wc_len)
void badTest5(const char* ptr,int wc_len)
{
int ret;
int len;
@@ -148,7 +148,7 @@ static void badTest5(const char* ptr,int wc_len)
}
}
static void badTest6(const char* ptr,int wc_len)
void badTest6(const char* ptr,int wc_len)
{
int ret;
int len;
@@ -171,7 +171,7 @@ static void badTest6(const char* ptr,int wc_len)
ptr+=ret;
}
}
static void badTest7(const char* ptr,int wc_len)
void badTest7(const char* ptr,int wc_len)
{
int ret;
int len;
@@ -188,7 +188,7 @@ static void badTest7(const char* ptr,int wc_len)
ptr+=ret;
}
}
static void badTest8(const char* ptr,wchar_t *wc)
void badTest8(const char* ptr,wchar_t *wc)
{
int ret;
int len;

View File

@@ -24,7 +24,7 @@ typedef unsigned int size_t;
void* calloc (size_t num, size_t size);
void* malloc (size_t size);
static void badTest1(void *src, int size) {
void badTest1(void *src, int size) {
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, (LPSTR)src, size, 0, 0); // BAD
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, (LPCWSTR)src, 30); // BAD
}
@@ -39,43 +39,43 @@ void goodTest2(){
}
printf("%s\n", dst);
}
static void badTest2(){
void badTest2(){
wchar_t src[] = L"0123456789ABCDEF";
char dst[16];
WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // BAD
printf("%s\n", dst);
}
static void goodTest3(){
void goodTest3(){
char src[] = "0123456789ABCDEF";
int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD
}
static void badTest3(){
void badTest3(){
char src[] = "0123456789ABCDEF";
int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
wchar_t * dst = (wchar_t*)calloc(size + 1, 1);
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD
}
static void goodTest4(){
void goodTest4(){
char src[] = "0123456789ABCDEF";
int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD
}
static void badTest4(){
void badTest4(){
char src[] = "0123456789ABCDEF";
int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
wchar_t * dst = (wchar_t*)malloc(size + 1);
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD
}
static int goodTest5(void *src){
int goodTest5(void *src){
return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 0, 0, 0); // GOOD
}
static int badTest5 (void *src) {
int badTest5 (void *src) {
return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 3, 0, 0); // BAD
}
static void goodTest6(WCHAR *src)
void goodTest6(WCHAR *src)
{
int size;
char dst[5] ="";
@@ -87,7 +87,7 @@ static void goodTest6(WCHAR *src)
WideCharToMultiByte(CP_ACP, 0, src, -1, dst, sizeof(dst), 0, 0); // GOOD
printf("%s\n", dst);
}
static void badTest6(WCHAR *src)
void badTest6(WCHAR *src)
{
char dst[5] ="";
WideCharToMultiByte(CP_ACP, 0, src, -1, dst, 260, 0, 0); // BAD

View File

@@ -11,14 +11,14 @@ size_t _mbstowcs_l(wchar_t *wcstr,const char *mbstr,size_t count, _locale_t loca
size_t mbsrtowcs(wchar_t *wcstr,const char *mbstr,size_t count, mbstate_t *mbstate);
static void badTest1(void *src, int size) {
void badTest1(void *src, int size) {
mbstowcs((wchar_t*)src,(char*)src,size); // BAD
_locale_t locale;
_mbstowcs_l((wchar_t*)src,(char*)src,size,locale); // BAD
mbstate_t *mbstate;
mbsrtowcs((wchar_t*)src,(char*)src,size,mbstate); // BAD
}
static void goodTest2(){
void goodTest2(){
char src[] = "0123456789ABCDEF";
wchar_t dst[16];
int res = mbstowcs(dst, src,16); // GOOD
@@ -29,43 +29,43 @@ static void goodTest2(){
}
printf("%s\n", dst);
}
static void badTest2(){
void badTest2(){
char src[] = "0123456789ABCDEF";
wchar_t dst[16];
mbstowcs(dst, src,16); // BAD
printf("%s\n", dst);
}
static void goodTest3(){
void goodTest3(){
char src[] = "0123456789ABCDEF";
int size = mbstowcs(NULL, src,NULL);
wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t));
mbstowcs(dst, src,size+1); // GOOD
}
static void badTest3(){
void badTest3(){
char src[] = "0123456789ABCDEF";
int size = mbstowcs(NULL, src,NULL);
wchar_t * dst = (wchar_t*)calloc(size + 1, 1);
mbstowcs(dst, src,size+1); // BAD
}
static void goodTest4(){
void goodTest4(){
char src[] = "0123456789ABCDEF";
int size = mbstowcs(NULL, src,NULL);
wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t));
mbstowcs(dst, src,size+1); // GOOD
}
static void badTest4(){
void badTest4(){
char src[] = "0123456789ABCDEF";
int size = mbstowcs(NULL, src,NULL);
wchar_t * dst = (wchar_t*)malloc(size + 1);
mbstowcs(dst, src,size+1); // BAD
}
static int goodTest5(void *src){
int goodTest5(void *src){
return mbstowcs(NULL, (char*)src,NULL); // GOOD
}
static int badTest5 (void *src) {
int badTest5 (void *src) {
return mbstowcs(NULL, (char*)src,3); // BAD
}
static void goodTest6(void *src){
void goodTest6(void *src){
wchar_t dst[5];
int size = mbstowcs(NULL, (char*)src,NULL);
if(size>=sizeof(dst)){
@@ -75,7 +75,7 @@ static void goodTest6(void *src){
mbstowcs(dst, (char*)src,sizeof(dst)); // GOOD
printf("%s\n", dst);
}
static void badTest6(void *src){
void badTest6(void *src){
wchar_t dst[5];
mbstowcs(dst, (char*)src,260); // BAD
printf("%s\n", dst);

View File

@@ -9,14 +9,14 @@ void goodTest1(unsigned char *src){
unsigned char dst[50];
_mbsnbcpy(dst,src,sizeof(dst)); // GOOD
}
static size_t badTest1(unsigned char *src){
size_t badTest1(unsigned char *src){
int cb = 0;
unsigned char dst[50];
while( cb < sizeof(dst) )
dst[cb++]=*src++; // BAD
return _mbclen(dst);
}
static void goodTest2(unsigned char *src){
void goodTest2(unsigned char *src){
int cb = 0;
unsigned char dst[50];
@@ -27,7 +27,7 @@ static void goodTest2(unsigned char *src){
src=_mbsinc(src);
}
}
static void badTest2(unsigned char *src){
void badTest2(unsigned char *src){
int cb = 0;
unsigned char dst[50];
@@ -38,11 +38,11 @@ static void badTest2(unsigned char *src){
src=_mbsinc(src);
}
}
static void goodTest3(){
void goodTest3(){
wchar_t name[50];
name[sizeof(name) / sizeof(*name) - 1] = L'\0'; // GOOD
}
static void badTest3(){
void badTest3(){
wchar_t name[50];
name[sizeof(name) - 1] = L'\0'; // BAD
}

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