mirror of
https://github.com/github/codeql.git
synced 2026-01-23 03:12:58 +01:00
Merge branch 'amammad-go-fastHttp' of https://github.com/amammad/codeql into amammad-go-fastHttp
This commit is contained in:
23
.github/dependabot.yml
vendored
23
.github/dependabot.yml
vendored
@@ -17,3 +17,26 @@ updates:
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types: ['version-update:semver-patch', 'version-update:semver-minor']
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "go/extractor"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
allow:
|
||||
- dependency-name: "golang.org/x/mod"
|
||||
- dependency-name: "golang.org/x/tools"
|
||||
groups:
|
||||
extractor-dependencies:
|
||||
patterns:
|
||||
- "golang.org/x/*"
|
||||
reviewers:
|
||||
- "github/codeql-go"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "go/ql/test"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
reviewers:
|
||||
- "github/codeql-go"
|
||||
|
||||
32
.github/workflows/check-change-note.yml
vendored
32
.github/workflows/check-change-note.yml
vendored
@@ -9,26 +9,42 @@ on:
|
||||
- "*/ql/lib/**/*.ql"
|
||||
- "*/ql/lib/**/*.qll"
|
||||
- "*/ql/lib/**/*.yml"
|
||||
- "shared/**/*.ql"
|
||||
- "shared/**/*.qll"
|
||||
- "!**/experimental/**"
|
||||
- "!ql/**"
|
||||
- ".github/workflows/check-change-note.yml"
|
||||
|
||||
jobs:
|
||||
check-change-note:
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
PULL_REQUEST_NUMBER: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Fail if no change note found. To fix, either add one, or add the `no-change-note-required` label.
|
||||
if: |
|
||||
github.event.pull_request.draft == false &&
|
||||
!contains(github.event.pull_request.labels.*.name, 'no-change-note-required')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
|
||||
grep true -c
|
||||
change_note_files=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '.[].filename | select(test("/change-notes/.*[.]md$"))')
|
||||
|
||||
if [ -z "$change_note_files" ]; then
|
||||
echo "No change note found. Either add one, or add the 'no-change-note-required' label."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Change notes found:"
|
||||
echo "$change_note_files"
|
||||
|
||||
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
|
||||
grep true -c
|
||||
bad_change_note_file_names=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))][] | select((test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$")) | not)')
|
||||
|
||||
if [ -n "$bad_change_note_file_names" ]; then
|
||||
echo "The following change note file names are invalid:"
|
||||
echo "$bad_change_note_file_names"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
4
.github/workflows/compile-queries.yml
vendored
4
.github/workflows/compile-queries.yml
vendored
@@ -29,9 +29,9 @@ jobs:
|
||||
# run with --check-only if running in a PR (github.sha != main)
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
|
||||
- name: compile queries - full
|
||||
# do full compile if running on main - this populates the cache
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
|
||||
|
||||
8
.github/workflows/csharp-qltest.yml
vendored
8
.github/workflows/csharp-qltest.yml
vendored
@@ -65,7 +65,10 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-2019]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
@@ -78,6 +81,7 @@ jobs:
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Extraction.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 autobuilder/Semmle.Autobuild.CSharp.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
shell: bash
|
||||
stubgentest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -87,7 +91,7 @@ jobs:
|
||||
run: |
|
||||
# Generate (Asp)NetCore stubs
|
||||
STUBS_PATH=stubs_output
|
||||
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger latest "$STUBS_PATH"
|
||||
python3 scripts/stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
|
||||
rm -rf ql/test/resources/stubs/_frameworks
|
||||
# Update existing stubs in the repo with the freshly generated ones
|
||||
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
|
||||
|
||||
25
.github/workflows/csv-coverage-pr-artifacts.yml
vendored
25
.github/workflows/csv-coverage-pr-artifacts.yml
vendored
@@ -89,9 +89,32 @@ jobs:
|
||||
- name: Save PR number
|
||||
run: |
|
||||
mkdir -p pr
|
||||
echo ${{ github.event.pull_request.number }} > pr/NR
|
||||
echo ${PR_NUMBER} > pr/NR
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
- name: Upload PR number
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pr
|
||||
path: pr/
|
||||
- name: Save comment ID (if it exists)
|
||||
run: |
|
||||
# Find the latest comment starting with COMMENT_PREFIX
|
||||
COMMENT_PREFIX=":warning: The head of this PR and the base branch were compared for differences in the framework coverage reports."
|
||||
COMMENT_ID=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate | jq --arg prefix "${COMMENT_PREFIX}" 'map(select(.body|startswith($prefix)) | .id) | max // empty')
|
||||
if [[ -z ${COMMENT_ID} ]]
|
||||
then
|
||||
echo "Comment not found. Not uploading 'comment/ID' artifact."
|
||||
else
|
||||
mkdir -p comment
|
||||
echo ${COMMENT_ID} > comment/ID
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
- name: Upload comment ID (if it exists)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: comment
|
||||
path: comment/
|
||||
if-no-files-found: ignore
|
||||
|
||||
65
.github/workflows/js-ml-tests.yml
vendored
65
.github/workflows/js-ml-tests.yml
vendored
@@ -1,65 +0,0 @@
|
||||
name: JS ML-powered queries tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: javascript/ql/experimental/adaptivethreatmodeling
|
||||
|
||||
jobs:
|
||||
qltest:
|
||||
name: Test QL
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Install pack dependencies
|
||||
run: |
|
||||
for pack in modelbuilding src test; do
|
||||
codeql pack install --mode verify -- "${pack}"
|
||||
done
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: js-ml-test
|
||||
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile \
|
||||
--check-only \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--threads=0 \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
lib modelbuilding src
|
||||
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run \
|
||||
--threads=0 \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
test
|
||||
@@ -6,7 +6,7 @@ provide:
|
||||
- "*/ql/consistency-queries/qlpack.yml"
|
||||
- "*/ql/automodel/src/qlpack.yml"
|
||||
- "*/ql/automodel/test/qlpack.yml"
|
||||
- "shared/*/qlpack.yml"
|
||||
- "shared/**/qlpack.yml"
|
||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||
- "go/ql/config/legacy-support/qlpack.yml"
|
||||
- "go/build/codeql-extractor-go/codeql-extractor.yml"
|
||||
@@ -29,6 +29,7 @@ provide:
|
||||
- "swift/extractor-pack/codeql-extractor.yml"
|
||||
- "swift/integration-tests/qlpack.yml"
|
||||
- "ql/extractor-pack/codeql-extractor.yml"
|
||||
- ".github/codeql/extensions/**/codeql-pack.yml"
|
||||
|
||||
versionPolicies:
|
||||
default:
|
||||
|
||||
@@ -498,22 +498,6 @@
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
|
||||
],
|
||||
"TaintedFormatStringQuery Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
|
||||
],
|
||||
"TaintedFormatStringCustomizations Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringCustomizations.qll"
|
||||
],
|
||||
"HttpToFileAccessQuery JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll"
|
||||
],
|
||||
"HttpToFileAccessCustomizations JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
|
||||
],
|
||||
"Typo database": [
|
||||
"javascript/ql/src/Expressions/TypoDatabase.qll",
|
||||
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
alias(
|
||||
name = "dbscheme",
|
||||
actual = "//cpp/ql/lib:dbscheme",
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "dbscheme-stats",
|
||||
actual = "//cpp/ql/lib:dbscheme-stats",
|
||||
)
|
||||
|
||||
pkg_filegroup(
|
||||
name = "db-files",
|
||||
srcs = [
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
class BuiltinType extends @builtintype {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
|
||||
where
|
||||
builtintypes(type, name, kind, size, sign, alignment) and
|
||||
if
|
||||
type instanceof @fp16 or
|
||||
type instanceof @std_bfloat16 or
|
||||
type instanceof @std_float16 or
|
||||
type instanceof @complex_std_float32 or
|
||||
type instanceof @complex_float32x or
|
||||
type instanceof @complex_std_float64 or
|
||||
type instanceof @complex_float64x or
|
||||
type instanceof @complex_std_float128
|
||||
then kind_new = 2
|
||||
else kind_new = kind
|
||||
select type, name, kind_new, size, sign, alignment
|
||||
2221
cpp/downgrades/5b388693c66db1e7dc2e76a90aa67a2b6eb74f0f/old.dbscheme
Normal file
2221
cpp/downgrades/5b388693c66db1e7dc2e76a90aa67a2b6eb74f0f/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Introduce new floating-point types from C23 and C++23
|
||||
compatibility: backwards
|
||||
builtintypes.rel: run builtintypes.qlo
|
||||
2231
cpp/downgrades/8cba93a44180e0d50a80a660950800d822b981fc/old.dbscheme
Normal file
2231
cpp/downgrades/8cba93a44180e0d50a80a660950800d822b981fc/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Removed @assignpaddexpr and @assignpsubexpr from @assign_bitwise_expr
|
||||
compatibility: full
|
||||
2226
cpp/downgrades/f79ce79e3b751aeeed59e594633ba5c07a27ef3e/old.dbscheme
Normal file
2226
cpp/downgrades/f79ce79e3b751aeeed59e594633ba5c07a27ef3e/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Introduce extractor version numbers
|
||||
compatibility: breaking
|
||||
extractor_version.rel: delete
|
||||
@@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files")
|
||||
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
pkg_files(
|
||||
name = "dbscheme",
|
||||
srcs = ["semmlecode.cpp.dbscheme"],
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
## 0.11.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* More field accesses are identified as `ImplicitThisFieldAccess`.
|
||||
* Added support for new floating-point types in C23 and C++23.
|
||||
|
||||
## 0.10.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
|
||||
## 0.10.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
|
||||
## 0.9.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
4
cpp/ql/lib/change-notes/2023-10-30-realloc-flow.md
Normal file
4
cpp/ql/lib/change-notes/2023-10-30-realloc-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added taint models for `realloc` and related functions.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The expressions `AssignPointerAddExpr` and `AssignPointerSubExpr` are no longer subtypes of `AssignBitwiseOperation`.
|
||||
9
cpp/ql/lib/change-notes/released/0.10.0.md
Normal file
9
cpp/ql/lib/change-notes/released/0.10.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.10.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
6
cpp/ql/lib/change-notes/released/0.10.1.md
Normal file
6
cpp/ql/lib/change-notes/released/0.10.1.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.10.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
14
cpp/ql/lib/change-notes/released/0.11.0.md
Normal file
14
cpp/ql/lib/change-notes/released/0.11.0.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 0.11.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* More field accesses are identified as `ImplicitThisFieldAccess`.
|
||||
* Added support for new floating-point types in C23 and C++23.
|
||||
14
cpp/ql/lib/change-notes/released/0.9.2.md
Normal file
14
cpp/ql/lib/change-notes/released/0.9.2.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 0.9.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
3
cpp/ql/lib/change-notes/released/0.9.3.md
Normal file
3
cpp/ql/lib/change-notes/released/0.9.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 0.11.0
|
||||
|
||||
3
cpp/ql/lib/experimental/cryptography/Concepts.qll
Normal file
3
cpp/ql/lib/experimental/cryptography/Concepts.qll
Normal file
@@ -0,0 +1,3 @@
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.modules.OpenSSL as OpenSSL
|
||||
239
cpp/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
239
cpp/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Names of known cryptographic algorithms.
|
||||
* The names are standardized into upper-case, no spaces, dashes or underscores.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a string to represent generally unknown algorithms.
|
||||
* Predicate is to be used to get a consistent string representation
|
||||
* for unknown algorithms.
|
||||
*/
|
||||
string unknownAlgorithm() { result = "UNKNOWN" }
|
||||
|
||||
string getHashType() { result = "HASH" }
|
||||
|
||||
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getKeyDerivationType() { result = "KEY_DERIVATION" }
|
||||
|
||||
string getCipherBlockModeType() { result = "BLOCK_MODE" }
|
||||
|
||||
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
|
||||
|
||||
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
|
||||
|
||||
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
|
||||
|
||||
string getSignatureType() { result = "SIGNATURE" }
|
||||
|
||||
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
|
||||
|
||||
string getAsymmetricType() {
|
||||
result in [
|
||||
getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(),
|
||||
getEllipticCurveType()
|
||||
]
|
||||
}
|
||||
|
||||
predicate isKnownType(string algType) {
|
||||
algType in [
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(),
|
||||
getKeyDerivationType(), getCipherBlockModeType(), getSymmetricPaddingType(),
|
||||
getAsymmetricPaddingType(), getEllipticCurveType(), getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
}
|
||||
|
||||
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
|
||||
|
||||
predicate isKnownAlgorithm(string name, string algType) {
|
||||
isHashingAlgorithm(name) and algType = "HASH"
|
||||
or
|
||||
isEncryptionAlgorithm(name, algType) and
|
||||
algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
or
|
||||
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
|
||||
or
|
||||
isCipherBlockModeAlgorithm(name) and algType = "BLOCK_MODE"
|
||||
or
|
||||
isPaddingAlgorithm(name, algType) and algType in ["SYMMETRIC_PADDING", "ASYMMETRIC_PADDING"]
|
||||
or
|
||||
isEllipticCurveAlgorithm(name) and algType = "ELLIPTIC_CURVE"
|
||||
or
|
||||
isSignatureAlgorithm(name) and algType = "SIGNATURE"
|
||||
or
|
||||
isKeyExchangeAlgorithm(name) and algType = "KEY_EXCHANGE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a known hashing algorithm in the model/library.
|
||||
*/
|
||||
predicate isHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S", "SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224",
|
||||
"SHA512256", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256",
|
||||
"SM3", "WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD",
|
||||
"RIPEMD128", "RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1", "MGF1SHA1",
|
||||
"MDC2", "SIPHASH"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name, string algType) {
|
||||
isAsymmetricEncryptionAlgorithm(name) and algType = "ASYMMETRIC_ENCRYPTION"
|
||||
or
|
||||
isSymmetricEncryptionAlgorithm(name) and algType = "SYMMETRIC_ENCRYPTION"
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name) { isEncryptionAlgorithm(name, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known symmetric encryption algorithm.
|
||||
*/
|
||||
predicate isSymmetricEncryptionAlgorithm(string name) {
|
||||
// NOTE: AES is meant to caputure all possible key lengths
|
||||
name =
|
||||
[
|
||||
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
|
||||
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
|
||||
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT", "SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES",
|
||||
"DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5",
|
||||
"RC5", "MAGMA", "KUZNYECHIK"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known key derivation algorithm.
|
||||
*/
|
||||
predicate isKeyDerivationAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"ARGON2", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC", "KBKDFCMAC", "BCRYPT", "HKDF",
|
||||
"HKDFEXPAND", "KBKDF", "KBKDFHMAC", "PBKDF1", "PBKDF2", "PBKDF2HMAC", "PKCS5", "SCRYPT",
|
||||
"X963KDF", "EVPKDF"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known cipher block mode
|
||||
*/
|
||||
predicate isCipherBlockModeAlgorithm(string name) {
|
||||
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX", "SIV", "ECB"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known padding algorithm
|
||||
*/
|
||||
predicate isPaddingAlgorithm(string name, string algType) {
|
||||
isSymmetricPaddingAlgorithm(name) and algType = "SYMMETRIC_PADDING"
|
||||
or
|
||||
isAsymmetricPaddingAlgorithm(name) and algType = "ASYMMETRIC_PADDING"
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if `name` corresponds to a known symmetric padding algorithm
|
||||
*/
|
||||
predicate isSymmetricPaddingAlgorithm(string name) { name = ["PKCS7", "ANSIX923"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric padding algorithm
|
||||
*/
|
||||
predicate isAsymmetricPaddingAlgorithm(string name) { name = ["OAEP", "PKCS1V15", "PSS", "KEM"] }
|
||||
|
||||
predicate isBrainpoolCurve(string curveName, int keySize) {
|
||||
// ALL BRAINPOOL CURVES
|
||||
keySize in [160, 192, 224, 256, 320, 384, 512] and
|
||||
(
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "R1"
|
||||
or
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "T1"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSecCurve(string curveName, int keySize) {
|
||||
// ALL SEC CURVES
|
||||
keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and
|
||||
exists(string suff | suff in ["R1", "R2", "K1"] |
|
||||
curveName = "SECT" + keySize.toString() + suff or
|
||||
curveName = "SECP" + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isC2Curve(string curveName, int keySize) {
|
||||
// ALL C2 CURVES
|
||||
keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and
|
||||
exists(string pre, string suff |
|
||||
pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"]
|
||||
|
|
||||
curveName = "C2" + pre + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isPrimeCurve(string curveName, int keySize) {
|
||||
// ALL PRIME CURVES
|
||||
keySize in [192, 239, 256] and
|
||||
exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize.toString() + suff)
|
||||
}
|
||||
|
||||
predicate isEllipticCurveAlgorithm(string curveName) { isEllipticCurveAlgorithm(curveName, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known elliptic curve.
|
||||
*/
|
||||
predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
|
||||
isSecCurve(curveName, keySize)
|
||||
or
|
||||
isBrainpoolCurve(curveName, keySize)
|
||||
or
|
||||
isC2Curve(curveName, keySize)
|
||||
or
|
||||
isPrimeCurve(curveName, keySize)
|
||||
or
|
||||
curveName = "ES256" and keySize = 256
|
||||
or
|
||||
curveName = "CURVE25519" and keySize = 255
|
||||
or
|
||||
curveName = "X25519" and keySize = 255
|
||||
or
|
||||
curveName = "ED25519" and keySize = 255
|
||||
or
|
||||
curveName = "CURVE448" and keySize = 448 // TODO: need to check the key size
|
||||
or
|
||||
curveName = "ED448" and keySize = 448
|
||||
or
|
||||
curveName = "X448" and keySize = 448
|
||||
or
|
||||
curveName = "NUMSP256T1" and keySize = 256
|
||||
or
|
||||
curveName = "NUMSP384T1" and keySize = 384
|
||||
or
|
||||
curveName = "NUMSP512T1" and keySize = 512
|
||||
or
|
||||
curveName = "SM2" and keySize in [256, 512]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known signature algorithm.
|
||||
*/
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256",
|
||||
"ECDSA384", "ECDSA512"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a key exchange algorithm.
|
||||
*/
|
||||
predicate isKeyExchangeAlgorithm(string name) {
|
||||
name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric encryption.
|
||||
*/
|
||||
predicate isAsymmetricEncryptionAlgorithm(string name) { name = ["RSA"] }
|
||||
316
cpp/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
316
cpp/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
@@ -0,0 +1,316 @@
|
||||
import cpp
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
|
||||
/*
|
||||
* A cryptographic artifact is a DataFlow::Node associated with some
|
||||
* operation, algorithm, or any other aspect of cryptography.
|
||||
*/
|
||||
|
||||
abstract class CryptographicArtifact extends Expr { }
|
||||
|
||||
// /**
|
||||
// * Associates a symmetric encryption algorithm with a block mode.
|
||||
// * The DataFlow::Node representing this association should be the
|
||||
// * point where the algorithm and block mode are combined.
|
||||
// * This may be at the call to encryption or in the construction
|
||||
// * of an object prior to encryption.
|
||||
// */
|
||||
// abstract class SymmetricCipher extends CryptographicArtifact{
|
||||
// abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
|
||||
// abstract BlockMode getBlockMode();
|
||||
// final predicate hasBlockMode(){
|
||||
// exists(this.getBlockMode())
|
||||
// }
|
||||
// }
|
||||
// /**
|
||||
// * A cryptographic operation is a method call that invokes a cryptographic
|
||||
// * algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
|
||||
// * (key generation).
|
||||
// *
|
||||
// * Since operations are related to or in support of algorithms, operations must
|
||||
// * provide a reference to their associated algorithm. Often operataions themselves
|
||||
// * encapsulate algorithms, so operations can also extend CryptographicAlgorithm
|
||||
// * and refer to themselves as the target algorithm.
|
||||
// */
|
||||
// abstract class CryptographicOperation extends CryptographicArtifact, Call{
|
||||
// // bindingset[paramName, ind]
|
||||
// // final DataFlow::Node getParameterSource(int ind, string paramName){
|
||||
// // result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
|
||||
// // }
|
||||
// final string getAlgorithmName(){
|
||||
// if exists(this.getAlgorithm().getName())
|
||||
// then result = this.getAlgorithm().getName()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
// final predicate hasAlgorithm(){
|
||||
// exists(this.getAlgorithm())
|
||||
// }
|
||||
// final predicate isUnknownAlgorithm(){
|
||||
// this.getAlgorithmName() = unknownAlgorithm()
|
||||
// or
|
||||
// not this.hasAlgorithm()
|
||||
// }
|
||||
// // TODO: this might have to be parameterized by a configuration source for
|
||||
// // situations where an operation is passed an algorithm
|
||||
// abstract CryptographicAlgorithm getAlgorithm();
|
||||
// }
|
||||
// /** A key generation operation for asymmetric keys */
|
||||
// abstract class KeyGen extends CryptographicOperation{
|
||||
// int getAKeySizeInBits(){
|
||||
// result = getKeySizeInBits(_)
|
||||
// }
|
||||
// final predicate hasKeySize(Expr configSrc){
|
||||
// exists(this.getKeySizeInBits(configSrc))
|
||||
// }
|
||||
// final predicate hasKeySize(){
|
||||
// exists(this.getAKeySizeInBits())
|
||||
// }
|
||||
// abstract Expr getKeyConfigSrc();
|
||||
// abstract int getKeySizeInBits(Expr configSrc);
|
||||
// }
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, Call { }
|
||||
|
||||
abstract class KeyGeneration extends CryptographicOperation {
|
||||
// TODO: what if the algorithm is UNKNOWN?
|
||||
abstract Expr getKeyConfigurationSource(CryptographicAlgorithm alg);
|
||||
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
int getKeySizeInBits(CryptographicAlgorithm alg) {
|
||||
result = this.getKeyConfigurationSource(alg).(Literal).getValue().toInt()
|
||||
}
|
||||
|
||||
predicate hasConstantKeySize(CryptographicAlgorithm alg) { exists(this.getKeySizeInBits(alg)) }
|
||||
|
||||
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
exists(this.getKeyConfigurationSource(alg))
|
||||
}
|
||||
|
||||
Expr getAKeyConfigurationSource() { result = this.getKeyConfigurationSource(_) }
|
||||
}
|
||||
|
||||
abstract class AsymmetricKeyGeneration extends KeyGeneration { }
|
||||
|
||||
abstract class SymmetricKeyGeneration extends KeyGeneration { }
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm is a `CryptographicArtifact`
|
||||
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
|
||||
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
|
||||
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
|
||||
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
|
||||
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
|
||||
*
|
||||
* In principle, this class should model the location where an algorithm enters the program, not
|
||||
* necessarily where it is used.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact {
|
||||
abstract string getName();
|
||||
|
||||
abstract string getAlgType();
|
||||
|
||||
// string getAlgType(){
|
||||
// if this instanceof HashAlgorithm then result = getHashType()
|
||||
// else if this instanceof KeyDerivationAlgorithm then result = getKeyDerivationType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricEncryptionType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricEncryptionType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricPaddingType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricPaddingType()
|
||||
// else if this instanceof EllipticCurveAlgorithm then result = getEllipticCurveType()
|
||||
// else if this instanceof BlockMode then result = getCipherBlockModeType()
|
||||
// else if this instanceof KeyExchangeAlgorithm then result = getKeyExchangeType()
|
||||
// else if this instanceof SigningAlgorithm then result = getSignatureType()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
// TODO: handle case where name isn't known, not just unknown?
|
||||
/**
|
||||
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
|
||||
* Subclassess should override for more api-specific normalization.
|
||||
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
|
||||
*/
|
||||
bindingset[s]
|
||||
string normalizeName(string s) {
|
||||
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
result = normStr and isKnownAlgorithm(result)
|
||||
or
|
||||
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
|
||||
)
|
||||
}
|
||||
|
||||
abstract Expr configurationSink();
|
||||
|
||||
predicate hasConfigurationSink() { exists(this.configurationSink()) }
|
||||
}
|
||||
|
||||
abstract class HashAlgorithm extends CryptographicAlgorithm {
|
||||
final string getHashName() {
|
||||
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
|
||||
then isHashingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getHashType() }
|
||||
}
|
||||
|
||||
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm {
|
||||
final string getKDFName() {
|
||||
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
|
||||
then isKeyDerivationAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getKeyDerivationType() }
|
||||
}
|
||||
|
||||
// abstract class KeyDerivationOperation extends CryptographicOperation{
|
||||
// DataFlow::Node getIterationSizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getSaltConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getHashConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
// // TODO: get encryption algorithm for CBC-based KDF?
|
||||
// DataFlow::Node getDerivedKeySizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getModeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// // TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
|
||||
// abstract predicate requiresIteration();
|
||||
// abstract predicate requiresSalt();
|
||||
// abstract predicate requiresHash();
|
||||
// //abstract predicate requiresKeySize(); // Going to assume all requires a size
|
||||
// abstract predicate requiresMode();
|
||||
// }
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
|
||||
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
|
||||
|
||||
final predicate isSymmetric() { not this.isAsymmetric() }
|
||||
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
|
||||
// class does not have this common predicate.
|
||||
}
|
||||
|
||||
/**
|
||||
* A parent class to represent any algorithm for which
|
||||
* asymmetric cryptography is involved.
|
||||
* Intended to be distinct from AsymmetricEncryptionAlgorithm
|
||||
* which is intended only for asymmetric algorithms that specifically encrypt.
|
||||
*/
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to asymmetric encryption,
|
||||
* e.g., RSA, DSA, but also RSA padding algorithms
|
||||
*/
|
||||
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
|
||||
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getAsymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to symmetric encryption,
|
||||
* e.g., AES, DES, but also block modes and padding
|
||||
*/
|
||||
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
// TODO: add a stream cipher predicate?
|
||||
override string getAlgType() { result = getSymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
// Used only to categorize all padding into a single object,
|
||||
// DO_NOT add predicates here. Only for categorization purposes.
|
||||
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
abstract class SymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
|
||||
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getSymmetricPaddingType() }
|
||||
}
|
||||
|
||||
abstract class AsymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
|
||||
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getAsymmetricPaddingType() }
|
||||
}
|
||||
|
||||
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getCurveName() {
|
||||
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
|
||||
then isEllipticCurveAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
|
||||
|
||||
override string getAlgType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
abstract class BlockModeAlgorithm extends CryptographicAlgorithm {
|
||||
final string getBlockModeName() {
|
||||
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
|
||||
then isCipherBlockModeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the IV configuration.
|
||||
*/
|
||||
abstract Expr getIVorNonce();
|
||||
|
||||
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
|
||||
|
||||
override string getAlgType() { result = getCipherBlockModeType() }
|
||||
}
|
||||
|
||||
// abstract class KeyWrapOperation extends CryptographicOperation{
|
||||
// }
|
||||
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm {
|
||||
final string getAuthticatedEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getKeyExchangeName() {
|
||||
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
|
||||
then isKeyExchangeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getKeyExchangeType() }
|
||||
}
|
||||
|
||||
abstract class SigningAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getSigningName() {
|
||||
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
|
||||
then isSignatureAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getSignatureType() }
|
||||
}
|
||||
718
cpp/ql/lib/experimental/cryptography/modules/OpenSSL.qll
Normal file
718
cpp/ql/lib/experimental/cryptography/modules/OpenSSL.qll
Normal file
@@ -0,0 +1,718 @@
|
||||
import cpp
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoFunction
|
||||
import experimental.cryptography.utils.OpenSSL.AlgorithmSink
|
||||
import experimental.cryptography.utils.OpenSSL.PassthroughFunction
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoAlgorithm
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
// import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Problematic case in OpenSSL speed.c
|
||||
* static const char *names[ALGOR_NUM] = {
|
||||
* "md2", "mdc2", "md4", "md5", "sha1", "rmd160",
|
||||
* "sha256", "sha512", "whirlpool", "hmac(md5)",
|
||||
* "des-cbc", "des-ede3", "rc4", "idea-cbc", "seed-cbc",
|
||||
* "rc2-cbc", "rc5-cbc", "blowfish", "cast-cbc",
|
||||
* "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
|
||||
* "camellia-128-cbc", "camellia-192-cbc", "camellia-256-cbc",
|
||||
* "evp", "ghash", "rand", "cmac"
|
||||
* };
|
||||
*
|
||||
* Every entry is considered a block mode, hash, and symmetric encryption algorithm
|
||||
* getEncryptionName for example, will return unknown
|
||||
*/
|
||||
predicate nodeToExpr(DataFlow::Node node, Expr e) {
|
||||
e = node.asExpr() or e = node.asIndirectArgument()
|
||||
}
|
||||
|
||||
Expr getExprFromNode(DataFlow::Node node) { nodeToExpr(node, result) }
|
||||
|
||||
DataFlow::Node getNodeFromExpr(Expr e) { nodeToExpr(result, e) }
|
||||
|
||||
predicate isEVP_PKEY_CTX(Type t) { t.getUnderlyingType().stripType().getName() = "evp_pkey_ctx_st" }
|
||||
|
||||
/**
|
||||
* An expression representing an EVP_PKEY_CTX* at the location of a
|
||||
* known AlgorithmSinkArgument.
|
||||
* The EVP_PKEY_CTX* represents the location where the CTX is tied to the algorithm,
|
||||
* and can be used as a source for tracing EVP_PKEY_CTX to other operations.
|
||||
*/
|
||||
class Known_EVP_PKEY_CTX_Ptr_Source extends Expr {
|
||||
Known_EVP_PKEY_CTX_Ptr_Source() {
|
||||
isEVP_PKEY_CTX(this.getUnderlyingType()) and
|
||||
this.getUnderlyingType() instanceof PointerType and
|
||||
exists(AlgorithmSinkArgument arg, Call sinkCall |
|
||||
arg.getSinkCall() = sinkCall and
|
||||
sinkCall.getAnArgument() = this
|
||||
or
|
||||
this = sinkCall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// module CTXFlow implements DataFlow::ConfigSig{
|
||||
// predicate isSource(DataFlow::Node source) {
|
||||
// // ASSUMPTION: at a sink, an algorithm is converted into a CTX through a return of the call only
|
||||
// // and is the primary source of interest for CTX tracing
|
||||
// source.asExpr() instanceof AlgorithmSinkArgument
|
||||
// }
|
||||
// predicate isSink(DataFlow::Node sink){
|
||||
// sink.asExpr() instanceof CTXSink
|
||||
// }
|
||||
// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// // cls.getName() = "asn1_object_st" flow out on any EVP_PKEY_CTX which is "evp_pkey_ctx_st"
|
||||
// exists(Call c |
|
||||
// isEVP_PKEY_CTX(c.getUnderlyingType()) and
|
||||
// node1.asExpr() = c.getAnArgument() and c = node2.asExpr())
|
||||
// }
|
||||
// }
|
||||
// module CTXFlowConfig = DataFlow::Global<CTXFlow>;
|
||||
// TODO: currently only handles tracing from literals to sinks
|
||||
module LiteralAlgorithmTracerConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof Literal and
|
||||
// Optimization to reduce literal tracing on integers to only those that are known/relevant NIDs.
|
||||
(
|
||||
exists(source.asExpr().getValue().toInt())
|
||||
implies
|
||||
source.asExpr().getValue().toInt() < getNIDMax()
|
||||
) and
|
||||
// False positives observed inside OBJ_nid2* and OBJ_sn2* functions where NULL is a possible assignment.
|
||||
// While this is a concern, it only occurs if the object being referenced is NULL to begin with
|
||||
// Perhaps a different query should be used to find these caes if they represent a threat.
|
||||
// Filter out any open ssl function source in a function namae Obj_*
|
||||
// False positives in OpenSSL also observed for CRYPTO_strndup (filtering any CRYPTO_* function)
|
||||
// due to setting a null byte in the string
|
||||
(
|
||||
isPossibleOpenSSLFunction(source.getEnclosingCallable())
|
||||
implies
|
||||
(
|
||||
not source.getEnclosingCallable().getName().matches("OBJ_%") and
|
||||
not source.getEnclosingCallable().getName().matches("CRYPTO_%")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// A sink is a call to a function that takes an algorithm as an argument
|
||||
// must include checks for asIndirectArgument since the input may be a pointer to an object
|
||||
// and the member of the object holds the algorithm on the trace.
|
||||
getExprFromNode(sink) instanceof AlgorithmSinkArgument
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
knownPassThroughStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// If the node is the 'next' argument of a isCallPassThrough, it is only allowed if it is an out parameter
|
||||
// i.e., a defining argument. This barrier says that if the node is an expression not an out parameter, it is filtered.
|
||||
// Out arguments will not be filtered.
|
||||
exists(Call c | knownPassthoughCall(c, _, node.asExpr()) and c.getAnArgument() = node.asExpr())
|
||||
or
|
||||
// False positive reducer, don't flow out through argv
|
||||
node.asVariable().hasName("argv")
|
||||
or
|
||||
node.asIndirectVariable().hasName("argv")
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
// Assume a read on crypto identifying field for any object of type asn1_object_st (i.e., ASN1_OBJECT)
|
||||
exists(Class cls | cls.getName() = "asn1_object_st" |
|
||||
node.getType().getUnspecifiedType().stripType() = cls and
|
||||
c.(DataFlow::FieldContent).getField() = cls.getAMember() and
|
||||
c.(DataFlow::FieldContent).getField().getName() in ["nid", "sn", "ln"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module LiteralAlgorithmTracer = DataFlow::Global<LiteralAlgorithmTracerConfig>;
|
||||
|
||||
/**
|
||||
* `source` is an expression that is a source of an algorithm of type `algType`.
|
||||
* `algType` may be `UNKONWN`.
|
||||
* See CryptoAlgorithmNames for other possible values of `algType`.
|
||||
*/
|
||||
bindingset[sinkAlgType]
|
||||
predicate hasLiteralPathToAlgSink(DataFlow::Node source, DataFlow::Node sink, string sinkAlgType) {
|
||||
LiteralAlgorithmTracer::flow(source, sink) and
|
||||
getExprFromNode(sink).(AlgorithmSinkArgument).algType() = sinkAlgType
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcSinkType) {
|
||||
knownTracedAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcType, string sinkType) {
|
||||
resolveAlgorithmFromLiteral(e, _, srcType) and
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
|
||||
isKnownType(sinkType) and
|
||||
isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcSinkType) {
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType
|
||||
then forall(string t | t = getAsymmetricType() | unknownTracedLiteralAlgorithm(e, t, srcSinkType))
|
||||
else unknownTracedLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcType, string sinkType) {
|
||||
// the literal resolves to an algorithm, but not to the sinktype
|
||||
// or generally doesn't resolve to any algorithm type
|
||||
// this case covers 'nonsense' cases e.g., use RSA for symmetric encryption
|
||||
not resolveAlgorithmFromLiteral(e, _, srcType) and
|
||||
isValidAlgorithmLiteral(e) and
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
|
||||
isKnownType(sinkType) and
|
||||
isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(AlgorithmSinkArgument e, string srcSinkType) {
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType
|
||||
then
|
||||
forall(string t | t = getAsymmetricType() | unknownTracedNonLiteralAlgorithm(e, t, srcSinkType))
|
||||
else unknownTracedNonLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(
|
||||
AlgorithmSinkArgument e, string srcType, string sinkType
|
||||
) {
|
||||
not hasLiteralPathToAlgSink(_, getNodeFromExpr(e), srcType) and
|
||||
LiteralAlgorithmTracerConfig::isSink(getNodeFromExpr(e)) and
|
||||
e.algType() = sinkType and
|
||||
isKnownType(srcType) and
|
||||
isKnownType(sinkType)
|
||||
}
|
||||
|
||||
private predicate functionAlgorithm(Call c, string algType) {
|
||||
isOpenSSLCryptoFunctionCall(c, _, algType)
|
||||
}
|
||||
|
||||
abstract class OpenSSLTracedAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { resolveAlgorithmFromLiteral(this, result, this.getAlgType()) }
|
||||
|
||||
override Expr configurationSink() {
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
||||
|
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLFunctionAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { isOpenSSLCryptoFunctionCall(this, result, this.getAlgType()) }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
|
||||
abstract class OpenSSLUnknownTracedLiteralAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() {
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
||||
|
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLUnknownTracedNonLiteralAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
|
||||
module SymmetricEncryption {
|
||||
abstract class OpenSSLSymmetricEncryptionAlgorithm extends SymmetricEncryptionAlgorithm { }
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionTracedAlgorithm() {
|
||||
knownTracedAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionFunctionAlgorithm() {
|
||||
functionAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module BlockModes {
|
||||
/**
|
||||
* In OpenSSL, block modes are associated directly with symmetric encryption algorithms.
|
||||
* As such, OpenSSLBLockModes are modeled as extensions of any openssl symmetric encryption algorithm
|
||||
*/
|
||||
class OpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLBlockModeAlgorithm() {
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
resolveAlgorithmFromLiteral(this, result, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, result, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override Expr configurationSink() {
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce() {
|
||||
// TODO
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownOpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
UnknownOpenSSLBlockModeAlgorithm() {
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
not resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE") and
|
||||
not isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() {
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
module Hashes {
|
||||
abstract class OpenSSLHashAlgorithm extends HashAlgorithm { }
|
||||
|
||||
class OpenSSLHashTracedAlgorithm extends OpenSSLTracedAlgorithm, OpenSSLHashAlgorithm {
|
||||
OpenSSLHashTracedAlgorithm() { knownTracedAlgorithm(this, getHashType()) }
|
||||
}
|
||||
|
||||
class OpenSSLHashFunctionAlgorithm extends OpenSSLFunctionAlgorithm, OpenSSLHashAlgorithm {
|
||||
OpenSSLHashFunctionAlgorithm() { functionAlgorithm(this, getHashType()) }
|
||||
}
|
||||
|
||||
class OpenSSLHashTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
OpenSSLHashAlgorithm
|
||||
{
|
||||
OpenSSLHashTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLHashUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
OpenSSLHashAlgorithm
|
||||
{
|
||||
OpenSSLHashUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLNullHash extends HashAlgorithm {
|
||||
OpenSSLNullHash() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getTarget().getName() in ["EVP_md_null"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
module EllipticCurves {
|
||||
// TODO: need to address EVP_PKEY_Q_keygen where the type is "EC" but the curve is UNKNOWN?
|
||||
class OpenSSLEllipticCurveTracedAlgorithm extends OpenSSLTracedAlgorithm, EllipticCurveAlgorithm {
|
||||
OpenSSLEllipticCurveTracedAlgorithm() { knownTracedAlgorithm(this, getEllipticCurveType()) }
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurveFunctionAlgorithm() { functionAlgorithm(this, getEllipticCurveType()) }
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_ex.html
|
||||
class OpenSSLNullEllipticCurve extends EllipticCurveAlgorithm {
|
||||
OpenSSLNullEllipticCurve() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getTarget().getName() in ["EC_KEY_new", "EC_KEY_new_ex"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
module AsymmetricEncryption {
|
||||
class OpenSSLAsymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionTracedAlgorithm() {
|
||||
knownTracedAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionFunctionAlgorithm() {
|
||||
functionAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module SigningAlgorithms {
|
||||
class OpenSSLSignatureTracedAlgorithm extends OpenSSLTracedAlgorithm, SigningAlgorithm {
|
||||
OpenSSLSignatureTracedAlgorithm() { knownTracedAlgorithm(this, getSignatureType()) }
|
||||
}
|
||||
|
||||
class OpenSSLSignatureFunctionAlgorithm extends OpenSSLFunctionAlgorithm, SigningAlgorithm {
|
||||
OpenSSLSignatureFunctionAlgorithm() { functionAlgorithm(this, getSignatureType()) }
|
||||
}
|
||||
|
||||
class OpenSSLSignatureTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
SigningAlgorithm
|
||||
{
|
||||
OpenSSLSignatureTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSignatureUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
SigningAlgorithm
|
||||
{
|
||||
OpenSSLSignatureUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module KeyExchange {
|
||||
class OpenSSLKeyExchangeTracedAlgorithm extends OpenSSLTracedAlgorithm, KeyExchangeAlgorithm {
|
||||
OpenSSLKeyExchangeTracedAlgorithm() { knownTracedAlgorithm(this, getKeyExchangeType()) }
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeFunctionAlgorithm extends OpenSSLFunctionAlgorithm, KeyExchangeAlgorithm {
|
||||
OpenSSLKeyExchangeFunctionAlgorithm() { functionAlgorithm(this, getKeyExchangeType()) }
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
KeyExchangeAlgorithm
|
||||
{
|
||||
OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
KeyExchangeAlgorithm
|
||||
{
|
||||
OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGeneration {
|
||||
/**
|
||||
* Functions that explicitly set key generation parameters.
|
||||
* `sizeInd` is the parameter specifying the size of the key.
|
||||
* `outInd` is the parameter or return value that the key is written to.
|
||||
* `outInd` is -1 if the key is written to the return value.
|
||||
*/
|
||||
predicate isAsymmetricKeyGenExplicitAlgorithm(Function func, int sizeInd, int outInd) {
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
name in [
|
||||
"EVP_PKEY_CTX_set_dsa_paramgen_bits", "DSA_generate_parameters_ex",
|
||||
"EVP_PKEY_CTX_set_rsa_keygen_bits", "RSA_generate_key_ex", "RSA_generate_key_fips",
|
||||
"EVP_PKEY_CTX_set_dh_paramgen_prime_len", "DH_generate_parameters_ex"
|
||||
] and
|
||||
sizeInd = 1 and
|
||||
outInd = 0
|
||||
or
|
||||
name in ["DSA_generate_parameters", "RSA_generate_key", "DH_generate_parameters"] and
|
||||
sizeInd = 0 and
|
||||
outInd = -1
|
||||
) and
|
||||
exists(Type t |
|
||||
(
|
||||
if sizeInd = -1
|
||||
then t = func.getType().getUnderlyingType()
|
||||
else t = func.getParameter(sizeInd).getUnderlyingType()
|
||||
) and
|
||||
t instanceof IntegralType and
|
||||
not t instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 8500
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c, int sizeInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), sizeInd, _) and
|
||||
c.getArgument(sizeInd) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlow = DataFlow::Global<AsymExplicitAlgKeyLengthFlowConfig>;
|
||||
|
||||
class OpenSSLAsymmetricKeyGenTiedToAlgorithm extends AsymmetricKeyGeneration {
|
||||
OpenSSLAsymmetricKeyGenTiedToAlgorithm() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), _, _)
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
alg = this and
|
||||
exists(int sizeInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(this.getTarget(), sizeInd, _) and
|
||||
AsymExplicitAlgKeyLengthFlow::flow(DataFlow::exprNode(result),
|
||||
DataFlow::exprNode(this.getArgument(sizeInd)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 5000
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c |
|
||||
c.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getArgument(3) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Flow =
|
||||
DataFlow::Global<Length_to_RSA_EVP_PKEY_Q_keygen_Config>;
|
||||
|
||||
class OpenSSL_RSA_EVP_PKEY_Q_keygen extends AsymmetricKeyGeneration {
|
||||
OpenSSL_RSA_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
this.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
this.getArgument(3).getUnderlyingType() instanceof IntegralType
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.configurationSink().(AlgorithmSinkArgument).getSinkCall() = this
|
||||
}
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
alg = this.getAlgorithm() and
|
||||
Length_to_RSA_EVP_PKEY_Q_keygen_Flow::flow(DataFlow::exprNode(result),
|
||||
DataFlow::exprNode(this.getArgument(3)))
|
||||
}
|
||||
}
|
||||
|
||||
predicate isKeyGenOperationWithNoSize(Function func) {
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
name in ["EVP_PKEY_keygen", "DSA_generate_key", "DH_generate_key", "EVP_PKEY_generate"]
|
||||
)
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
exists(Call c, Function func, int outInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(func, _, outInd) and
|
||||
c.getTarget() = func
|
||||
|
|
||||
if outInd = -1 then node.asExpr() = c else node.asExpr() = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(Call c |
|
||||
isKeyGenOperationWithNoSize(c.getTarget()) and c.getAnArgument() = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenFlow = DataFlow::Global<KeyGenKeySizeInitToKeyGenConfig>;
|
||||
|
||||
predicate isEVP_PKEY_CTX_Source(DataFlow::Node node, CryptographicAlgorithm alg) {
|
||||
exists(Call c |
|
||||
alg.configurationSink().(AlgorithmSinkArgument).getSinkCall() = c and
|
||||
(
|
||||
node.asExpr() = c
|
||||
or
|
||||
node.asExpr() = c.getAnArgument()
|
||||
or
|
||||
node.asDefiningArgument() = c.getAnArgument()
|
||||
)
|
||||
) and
|
||||
(
|
||||
node.asExpr() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
or
|
||||
node.asDefiningArgument() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
)
|
||||
}
|
||||
|
||||
predicate isKeyGen_EVP_PKEY_CTX_Sink(DataFlow::Node node, Call c) {
|
||||
isKeyGenOperationWithNoSize(c.getTarget()) and nodeToExpr(node, c.getAnArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace from EVP_PKEY_CTX* at algorithm sink to keygen,
|
||||
* users can then extrapolatae the matching algorithm from the alg sink to the keygen
|
||||
*/
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isEVP_PKEY_CTX_Source(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isKeyGen_EVP_PKEY_CTX_Sink(sink, _) }
|
||||
}
|
||||
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow =
|
||||
DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize>;
|
||||
|
||||
/**
|
||||
* UNKNOWN key sizes to general purpose key generation functions (i.e., that take in no key size and assume
|
||||
* is it set on context prior to the call). No path from a key configuration to these operations
|
||||
* means the key size is UNKNOWN, or more precisely the key size is DEFAULT but
|
||||
* the defaults can change with each version of OpenSSL, we simply assume the size is generally UNKNOWN.
|
||||
* ASSUMPTION/TODO: we currently model all known locations where a key size is set explicitly.
|
||||
* When a key is set implicitly, this usually means a key generation operation
|
||||
* is called where the operation takes in no key size, and no flow to this operation
|
||||
* initializes the context with a key size.
|
||||
* Currently, without a definitive source (set of sources) to start tracing from, we cannot determine
|
||||
* determine if a single path exists that initializes the context with a key size and another that doesn't.
|
||||
* Rather than attempt to model all possible sources, we assume that if no path
|
||||
* from a key config location reaches a generic key generation operation, then the key size is not set.
|
||||
* NOTE: while this is true, it is possible a key size is set in one path, but not in another
|
||||
* meaning this approach (and other similar approaches used in this model for UNKNOWN)
|
||||
* can produce false negatives.
|
||||
*/
|
||||
class OpenSSLDefaultKeyGeneration extends AsymmetricKeyGeneration {
|
||||
OpenSSLDefaultKeyGeneration() {
|
||||
// this is a call to a function matching isKeyGenOperationWithNoSize
|
||||
// and there is no flow from a key configuration source to this call
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isKeyGenOperationWithNoSize(this.getTarget()) and
|
||||
not exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
KeyGenKeySizeInitToKeyGenFlow::flow(src, sink) and
|
||||
nodeToExpr(sink, this.getAnArgument())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
if this.getTarget().getName() in ["DSA_generate_key", "DH_generate_key"]
|
||||
then result = this
|
||||
else
|
||||
// NOTE/ASSUMPTION: EVP_PKEY_keygen, EVP_PKEY_generate assume only other possibilities,
|
||||
// each take in a CTX as the first arg, need to trace from an alg sink from this CTX param
|
||||
// get every alg sink, get the corresponding call, trace out on any CTX type variable
|
||||
// to the key gen
|
||||
// NOTE: looking for any cryptographic algorithm tracing to the keygen to handle
|
||||
// any odd cases we aren't awaare of where keygen can be used for other algorithm types
|
||||
exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow::flow(src, sink) and
|
||||
isEVP_PKEY_CTX_Source(src, result) and
|
||||
isKeyGen_EVP_PKEY_CTX_Sink(sink, this)
|
||||
// TODO: what if there is no CTX source? then the keygen becomes an UNKNOWN sink
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* For this class, there is no known configuration source for any algorithm
|
||||
*/
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) { none() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* Predicates/classes for identifying algorithm sinks.
|
||||
* An Algorithm Sink is a function that takes an algorithm as an argument.
|
||||
* In particular, any function that takes in an algorithm that until the call
|
||||
* the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
|
||||
*/
|
||||
|
||||
//TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc.
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType }
|
||||
|
||||
abstract class AlgorithmSinkArgument extends Expr {
|
||||
AlgorithmSinkArgument() {
|
||||
exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function call in which the argument exists
|
||||
*/
|
||||
Call getSinkCall() { result.getAnArgument() = this }
|
||||
|
||||
abstract string algType();
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
|
||||
predicate cipherAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0
|
||||
or
|
||||
funcName = "EVP_CIPHER_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class CipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
CipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
cipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
|
||||
predicate macAlgorithmSink(string funcName, int argInd) {
|
||||
(funcName = "EVP_MAC_fetch" and argInd = 1)
|
||||
}
|
||||
|
||||
class MACAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MACAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
macAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
|
||||
predicate messageDigestAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0
|
||||
or
|
||||
funcName = "EVP_MD_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MessageDigestAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
messageDigestAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getHashType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
|
||||
predicate keyExchangeAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
|
||||
or
|
||||
funcName = "EVP_KEM_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyExchangeAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyExchangeAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyExchangeType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
|
||||
predicate keyManagementAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyManagementAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyManagementAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KDF
|
||||
predicate keyDerivationAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KDF_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyDerivationAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyDerivationAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyDerivationType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
|
||||
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1
|
||||
or
|
||||
funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3
|
||||
// NOTE: other cases are handled by AsymmetricAlgorithmSink
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
asymmetricCipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
|
||||
predicate randomAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_RAND_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class RandomAlgorithmSink extends AlgorithmSinkArgument {
|
||||
RandomAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
randomAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
|
||||
predicate signatureAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class SignatureAlgorithmSink extends AlgorithmSinkArgument {
|
||||
SignatureAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
signatureAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSignatureType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
|
||||
predicate ellipticCurveAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
|
||||
or
|
||||
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
|
||||
or
|
||||
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
|
||||
}
|
||||
|
||||
class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
ellipticCurveAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
|
||||
* ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
|
||||
*/
|
||||
class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
|
||||
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
|
||||
// https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
predicate asymmetricAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
|
||||
or
|
||||
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
|
||||
or
|
||||
funcName in [
|
||||
"EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"
|
||||
] and
|
||||
argInd = 0
|
||||
or
|
||||
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
|
||||
or
|
||||
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
|
||||
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
|
||||
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
|
||||
// TODO consider void cases EVP_PKEY_new
|
||||
}
|
||||
|
||||
class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
asymmetricAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
|
||||
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(2)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
not exists(c.getArgument(3))
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
predicate inferredOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
|
||||
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
normalizeFunctionName(f, algType) = normalized
|
||||
}
|
||||
|
||||
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType) {
|
||||
// NOTE: relying on inference as there are thousands of functions for crypto
|
||||
// enumerating them all and maintaining the list seems problematic.
|
||||
// For now, we will rely on dynamically inferring algorithms for function names.
|
||||
// This has been seen to be reasonably efficient and accurate.
|
||||
inferredOpenSSLCryptoFunction(f, normalized, algType)
|
||||
}
|
||||
|
||||
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
|
||||
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
result = basicNormalizeFunctionName(f, algType) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
private string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
(
|
||||
(result.length() <= 4 and result != "DESX")
|
||||
implies
|
||||
f.getName().toUpperCase().matches("%" + result + "%")
|
||||
) and
|
||||
(
|
||||
(result.length() <= 4 and result = "DESX")
|
||||
implies
|
||||
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
|
||||
) and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(
|
||||
result.matches("DES")
|
||||
implies
|
||||
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
|
||||
) and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
(
|
||||
result.matches("SEED")
|
||||
implies
|
||||
not exists(string s |
|
||||
s in [
|
||||
"SEED_SRC_GENERATE", "RAND", "NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED",
|
||||
"GET0_SEED", "RESEED", "SEEDING"
|
||||
]
|
||||
|
|
||||
f.getName().toUpperCase().matches("%" + s + "%")
|
||||
)
|
||||
) and
|
||||
// ARIA specific false positives
|
||||
(result.matches("ARIA") implies not f.getName().toUpperCase().matches("%VARIANT%")) and
|
||||
// CTR false positives
|
||||
(result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%")) and
|
||||
// ES false positives (e.g., ES256 from AES256)
|
||||
(result.matches("ES%") implies not f.getName().toUpperCase().matches("%AES%")) and
|
||||
// RSA false positives
|
||||
(result.matches("RSA") implies not f.getName().toUpperCase().matches("%UNIVERSAL%")) and
|
||||
//rsaz functions deemed to be too low level, and can be ignored
|
||||
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
|
||||
// General False positives
|
||||
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
|
||||
not f.getName().toLowerCase().matches("%get%")
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* This file contains predicates create to build up initial data sets for OpenSSL
|
||||
* predicates. E.g., These predicates were used to assist in associating all
|
||||
* openSSL functions with their known crypto algorithms.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoFunction
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
result = basicNormalizeFunctionName(f, algType) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(
|
||||
result.matches("ES256")
|
||||
implies
|
||||
not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
openSSLLibraryFunc(f) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
(
|
||||
(result.length() <= 4 and result != "DESX")
|
||||
implies
|
||||
f.getName().toUpperCase().matches("%" + result + "%")
|
||||
) and
|
||||
(
|
||||
(result.length() <= 4 and result = "DESX")
|
||||
implies
|
||||
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
|
||||
) and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(
|
||||
result.matches("DES")
|
||||
implies
|
||||
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
|
||||
) and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
(
|
||||
result.matches("%SEED%")
|
||||
implies
|
||||
not not exists(string s |
|
||||
s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"]
|
||||
|
|
||||
f.getName().toUpperCase().matches("%" + s + "%")
|
||||
)
|
||||
) and
|
||||
// ARIA specific false positives
|
||||
(result.matches("%ARIA%") implies not f.getName().toUpperCase().matches("%VARIANT%"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate to support name normalization.
|
||||
* Converts the raw name upper-case with no hyphen, slash, underscore, hash, or space.
|
||||
* Looks for substrings that are known algorithms, and normalizes the name.
|
||||
* If the algorithm cannot be determined or is in the ignorable list (`isIgnorableOpenSSLAlgorithm`)
|
||||
* this predicate will not resolve a name.
|
||||
*
|
||||
* Rationale for private: For normalization, we want to get the longest string for a normalized name match
|
||||
* for a given algorithm type. I found this easier to express if the public normalizeName
|
||||
* checks that the name is the longest, and that UNKNOWN is reserved if there exists no
|
||||
* result from this predicate that is known.
|
||||
*/
|
||||
bindingset[name]
|
||||
string privateNormalizeName(string name, string algType) {
|
||||
//not isIgnorableOpenSSLAlgorithm(name, _, _) and
|
||||
// targetOpenSSLAlgorithm(name, _) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `name` is the name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
bindingset[name]
|
||||
string normalizeName(string name, string algType) {
|
||||
(
|
||||
if exists(privateNormalizeName(name, _))
|
||||
then result = privateNormalizeName(name, algType)
|
||||
else (
|
||||
result = unknownAlgorithm() and algType = "UNKNOWN"
|
||||
)
|
||||
) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = privateNormalizeName(name, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(
|
||||
result.matches("ES256")
|
||||
implies
|
||||
not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%"))
|
||||
)
|
||||
}
|
||||
11292
cpp/ql/lib/experimental/cryptography/utils/OpenSSL/LibraryFunction.qll
Normal file
11292
cpp/ql/lib/experimental/cryptography/utils/OpenSSL/LibraryFunction.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
// TODO: possible use of extensible predicates here
|
||||
// NOTE: -1 for outInd represents the return value
|
||||
predicate knownPassthroughFunction(Function f, int inInd, int outInd) {
|
||||
// Trace through functions
|
||||
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
|
||||
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
|
||||
openSSLLibraryFunc(f) and
|
||||
(
|
||||
f.getName() in [
|
||||
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn", "OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
|
||||
"OBJ_txt2nid", "OBJ_txt2obj", "OBJ_dup", "EVP_CIPHER_get0_name"
|
||||
] and
|
||||
inInd = 0 and
|
||||
outInd = -1
|
||||
or
|
||||
f.getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
|
||||
inInd = 2 and
|
||||
outInd = 0
|
||||
or
|
||||
// Dup/copy pattern occurs in more places,
|
||||
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
|
||||
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
|
||||
or
|
||||
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `c` is a call to a function that preserves the algorithm but changes its form.
|
||||
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
|
||||
*/
|
||||
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr) {
|
||||
exists(int inInd, int outInd |
|
||||
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
|
||||
inExpr = c.getArgument(inInd) and
|
||||
if outInd = -1 then outExpr = c else outExpr = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
|
||||
*/
|
||||
|
||||
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(Expr cur, Expr next |
|
||||
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
|
||||
(
|
||||
next = node2.asExpr() or
|
||||
next = node2.asIndirectArgument() or
|
||||
next = node2.asDefiningArgument()
|
||||
)
|
||||
|
|
||||
exists(Call c | knownPassthoughCall(c, cur, next))
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.9.2-dev
|
||||
version: 0.11.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
@@ -7,6 +7,7 @@ library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/rangeanalysis: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
|
||||
@@ -5,155 +5,35 @@
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
private import codeql.util.FileSystem
|
||||
|
||||
private module Input implements InputSig {
|
||||
abstract class ContainerBase extends @container {
|
||||
abstract string getAbsolutePath();
|
||||
|
||||
ContainerBase getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
class FolderBase extends ContainerBase, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
class FileBase extends ContainerBase, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
predicate hasSourceLocationPrefix = sourceLocationPrefix/1;
|
||||
}
|
||||
|
||||
private module Impl = Make<Input>;
|
||||
|
||||
/** A file or folder. */
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* Gets the absolute, canonical path of this container, using forward slashes
|
||||
* as path separator.
|
||||
*
|
||||
* The path starts with a _root prefix_ followed by zero or more _path
|
||||
* segments_ separated by forward slashes.
|
||||
*
|
||||
* The root prefix is of one of the following forms:
|
||||
*
|
||||
* 1. A single forward slash `/` (Unix-style)
|
||||
* 2. An upper-case drive letter followed by a colon and a forward slash,
|
||||
* such as `C:/` (Windows-style)
|
||||
* 3. Two forward slashes, a computer name, and then another forward slash,
|
||||
* such as `//FileServer/` (UNC-style)
|
||||
*
|
||||
* Path segments are never empty (that is, absolute paths never contain two
|
||||
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
|
||||
* segments never contain forward slashes, and no path segment is of the
|
||||
* form `.` (one dot) or `..` (two dots).
|
||||
*
|
||||
* Note that an absolute path never ends with a forward slash, except if it is
|
||||
* a bare root prefix, that is, the path has no path segments. A container
|
||||
* whose absolute path has no segments is always a `Folder`, not a `File`.
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* 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):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Base name</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst.js"</td></tr>
|
||||
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
|
||||
* <tr><td>"/"</td><td>""</td></tr>
|
||||
* <tr><td>"C:/"</td><td>""</td></tr>
|
||||
* <tr><td>"D:/"</td><td>""</td></tr>
|
||||
* <tr><td>"//FileServer/"</td><td>""</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getBaseName() {
|
||||
result = 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):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Extension</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"js"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() {
|
||||
result = 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):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Stem</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() { result = 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.
|
||||
*/
|
||||
override string toString() { result = this.getAbsolutePath() }
|
||||
class Container extends ElementBase, Impl::Container {
|
||||
override string toString() { result = Impl::Container.super.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,14 +46,7 @@ class Container extends Locatable, @container {
|
||||
*
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
class Folder extends Container, Impl::Folder {
|
||||
override string getAPrimaryQlClass() { result = "Folder" }
|
||||
}
|
||||
|
||||
@@ -189,9 +62,7 @@ class Folder extends Container, @folder {
|
||||
* The base name further decomposes into the _stem_ and _extension_ -- see
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
class File extends Container, Locatable, Impl::File {
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
|
||||
override Location getLocation() {
|
||||
|
||||
@@ -158,9 +158,7 @@ class NameQualifyingElement extends Element, @namequalifyingelement {
|
||||
/**
|
||||
* A special name-qualifying element. For example: `__super`.
|
||||
*/
|
||||
library class SpecialNameQualifyingElement extends NameQualifyingElement,
|
||||
@specialnamequalifyingelement
|
||||
{
|
||||
class SpecialNameQualifyingElement extends NameQualifyingElement, @specialnamequalifyingelement {
|
||||
/** Gets the name of this special qualifying element. */
|
||||
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
|
||||
|
||||
|
||||
@@ -819,6 +819,30 @@ private predicate floatingPointTypeMapping(
|
||||
or
|
||||
// _Complex _Float16
|
||||
kind = 53 and base = 2 and domain = TComplexDomain() and realKind = 52 and extended = false
|
||||
or
|
||||
// __fp16
|
||||
kind = 54 and base = 2 and domain = TRealDomain() and realKind = 54 and extended = false
|
||||
or
|
||||
// __bf16
|
||||
kind = 55 and base = 2 and domain = TRealDomain() and realKind = 55 and extended = false
|
||||
or
|
||||
// std::float16_t
|
||||
kind = 56 and base = 2 and domain = TRealDomain() and realKind = 56 and extended = false
|
||||
or
|
||||
// _Complex _Float32
|
||||
kind = 57 and base = 2 and domain = TComplexDomain() and realKind = 45 and extended = false
|
||||
or
|
||||
// _Complex _Float32x
|
||||
kind = 58 and base = 2 and domain = TComplexDomain() and realKind = 46 and extended = true
|
||||
or
|
||||
// _Complex _Float64
|
||||
kind = 59 and base = 2 and domain = TComplexDomain() and realKind = 47 and extended = false
|
||||
or
|
||||
// _Complex _Float64x
|
||||
kind = 60 and base = 2 and domain = TComplexDomain() and realKind = 48 and extended = true
|
||||
or
|
||||
// _Complex _Float128
|
||||
kind = 61 and base = 2 and domain = TComplexDomain() and realKind = 49 and extended = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,6 +73,10 @@ private int isSource(Expr bufferExpr, Element why) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Same as `getBufferSize`, but with the `why` column projected away to prevent large duplications. */
|
||||
pragma[nomagic]
|
||||
int getBufferSizeProj(Expr bufferExpr) { result = getBufferSize(bufferExpr, _) }
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
@@ -87,7 +91,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferSize - parentClass.getSize()
|
||||
result = getBufferSizeProj(parentPtr) + bufferSize - parentClass.getSize()
|
||||
|
|
||||
if exists(bufferVar.getType().getSize())
|
||||
then bufferSize = bufferVar.getType().getSize()
|
||||
@@ -95,7 +99,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
)
|
||||
or
|
||||
// dataflow (all sources must be the same size)
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSize(def, _)) and
|
||||
// find reason
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSizeProj(def)) and
|
||||
exists(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | exists(getBufferSize(def, why)))
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ predicate canValueFlow(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = toExpr.(ConditionalExpr).getElse()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for AnalyzedString */
|
||||
deprecated class AnalysedString = AnalyzedString;
|
||||
|
||||
/**
|
||||
* An analyzed null terminated string.
|
||||
*/
|
||||
|
||||
@@ -78,7 +78,7 @@ predicate parameterUsePair(Parameter p, VariableAccess va) {
|
||||
/**
|
||||
* Utility class: A definition or use of a stack variable.
|
||||
*/
|
||||
library class DefOrUse extends ControlFlowNodeBase {
|
||||
class DefOrUse extends ControlFlowNodeBase {
|
||||
DefOrUse() {
|
||||
// Uninstantiated templates are purely syntax, and only on instantiation
|
||||
// will they be complete with information about types, conversions, call
|
||||
@@ -140,7 +140,7 @@ library class DefOrUse extends ControlFlowNodeBase {
|
||||
}
|
||||
|
||||
/** A definition of a stack variable. */
|
||||
library class Def extends DefOrUse {
|
||||
class Def extends DefOrUse {
|
||||
Def() { definition(_, this) }
|
||||
|
||||
override SemanticStackVariable getVariable(boolean isDef) {
|
||||
@@ -155,7 +155,7 @@ private predicate parameterIsOverwritten(Function f, Parameter p) {
|
||||
}
|
||||
|
||||
/** A definition of a parameter. */
|
||||
library class ParameterDef extends DefOrUse {
|
||||
class ParameterDef extends DefOrUse {
|
||||
ParameterDef() {
|
||||
// Optimization: parameters that are not overwritten do not require
|
||||
// reachability analysis
|
||||
@@ -169,7 +169,7 @@ library class ParameterDef extends DefOrUse {
|
||||
}
|
||||
|
||||
/** A use of a stack variable. */
|
||||
library class Use extends DefOrUse {
|
||||
class Use extends DefOrUse {
|
||||
Use() { useOfVar(_, this) }
|
||||
|
||||
override SemanticStackVariable getVariable(boolean isDef) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import SSAUtils
|
||||
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
|
||||
* This class provides the standard SSA logic.
|
||||
*/
|
||||
library class StandardSsa extends SsaHelper {
|
||||
class StandardSsa extends SsaHelper {
|
||||
StandardSsa() { this = 0 }
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ private predicate live_at_exit_of_bb(StackVariable v, BasicBlock b) {
|
||||
|
||||
/** Common SSA logic for standard SSA and range-analysis SSA. */
|
||||
cached
|
||||
library class SsaHelper extends int {
|
||||
class SsaHelper extends int {
|
||||
/* 0 = StandardSSA, 1 = RangeSSA */
|
||||
cached
|
||||
SsaHelper() { this in [0 .. 1] }
|
||||
|
||||
@@ -366,12 +366,12 @@ class CompileTimeConstantInt extends Expr {
|
||||
int getIntValue() { result = val }
|
||||
}
|
||||
|
||||
library class CompileTimeVariableExpr extends Expr {
|
||||
class CompileTimeVariableExpr extends Expr {
|
||||
CompileTimeVariableExpr() { not this instanceof CompileTimeConstantInt }
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of expressions. */
|
||||
library class ExprEvaluator extends int {
|
||||
class ExprEvaluator extends int {
|
||||
/*
|
||||
* 0 = ConditionEvaluator,
|
||||
* 1 = SwitchEvaluator,
|
||||
@@ -956,7 +956,7 @@ private predicate returnStmt(Function f, Expr value) {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of conditions. */
|
||||
library class ConditionEvaluator extends ExprEvaluator {
|
||||
class ConditionEvaluator extends ExprEvaluator {
|
||||
ConditionEvaluator() { this = 0 }
|
||||
|
||||
override predicate interesting(Expr e) {
|
||||
@@ -967,7 +967,7 @@ library class ConditionEvaluator extends ExprEvaluator {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of switch expressions. */
|
||||
library class SwitchEvaluator extends ExprEvaluator {
|
||||
class SwitchEvaluator extends ExprEvaluator {
|
||||
SwitchEvaluator() { this = 1 }
|
||||
|
||||
override predicate interesting(Expr e) { e = getASwitchExpr(_, _) }
|
||||
@@ -976,7 +976,7 @@ library class SwitchEvaluator extends ExprEvaluator {
|
||||
private int getSwitchValue(Expr e) { exists(SwitchEvaluator x | result = x.getValue(e)) }
|
||||
|
||||
/** A helper class for evaluation of loop entry conditions. */
|
||||
library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
LoopEntryConditionEvaluator() { this in [2 .. 3] }
|
||||
|
||||
abstract override predicate interesting(Expr e);
|
||||
@@ -1149,7 +1149,7 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of while-loop entry conditions. */
|
||||
library class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
WhileLoopEntryConditionEvaluator() { this = 2 }
|
||||
|
||||
override predicate interesting(Expr e) { exists(WhileStmt while | e = while.getCondition()) }
|
||||
@@ -1162,7 +1162,7 @@ library class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluat
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of for-loop entry conditions. */
|
||||
library class ForLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
class ForLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
ForLoopEntryConditionEvaluator() { this = 3 }
|
||||
|
||||
override predicate interesting(Expr e) { exists(ForStmt for | e = for.getCondition()) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -874,28 +874,3 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
*
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
deprecated class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(SsaDefinition def, Variable v, boolean branch |
|
||||
result.getExpr() = def.getAUse(v) and
|
||||
this.checks(def.getAUse(v), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -306,15 +306,13 @@ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.get
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
* Note: the C++ front-end often automatically desugars `field` to
|
||||
* `this->field`, so most accesses of `this->field` are instances
|
||||
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
|
||||
* `ImplicitThisFieldAccess`.
|
||||
*/
|
||||
class ImplicitThisFieldAccess extends FieldAccess {
|
||||
override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" }
|
||||
|
||||
ImplicitThisFieldAccess() { not exists(this.getQualifier()) }
|
||||
ImplicitThisFieldAccess() {
|
||||
this.getQualifier().(ThisExpr).isCompilerGenerated() or not exists(this.getQualifier())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +330,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
|
||||
// access without a qualifier. The only other unqualified field accesses it
|
||||
// emits are for compiler-generated constructors and destructors. When we
|
||||
// filter those out, there are only pointer-to-field literals left.
|
||||
not this.isCompilerGenerated()
|
||||
not this.isCompilerGenerated() and not exists(this.getQualifier())
|
||||
}
|
||||
|
||||
override predicate isConstant() { any() }
|
||||
|
||||
15
cpp/ql/lib/semmle/code/cpp/internal/ExtractorVersion.qll
Normal file
15
cpp/ql/lib/semmle/code/cpp/internal/ExtractorVersion.qll
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* INTERNAL: Do not use. Provides predicates for getting the CodeQL and frontend
|
||||
* version used during database extraction.
|
||||
*/
|
||||
|
||||
/** Get the extractor CodeQL version */
|
||||
string getExtractorCodeQLVersion() { extractor_version(result, _) }
|
||||
|
||||
/** Get the extractor frontend version */
|
||||
string getExtractorFrontendVersion() { extractor_version(_, result) }
|
||||
|
||||
predicate isExtractorFrontendVersion65OrHigher() {
|
||||
// Version numbers we not included in the database before 6.5.
|
||||
exists(getExtractorCodeQLVersion())
|
||||
}
|
||||
@@ -7,9 +7,12 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*
|
||||
* This predicate does not take additional call targets
|
||||
* from `AdditionalCallTarget` into account.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
DataFlowCallable defaultViableCallable(DataFlowCall call) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
@@ -29,6 +32,17 @@ DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = defaultViableCallable(call)
|
||||
or
|
||||
// Additional call targets
|
||||
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides virtual dispatch support compatible with the original
|
||||
* implementation of `semmle.code.cpp.security.TaintTracking`.
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,15 +250,17 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -555,7 +555,7 @@ predicate instructionForFullyConvertedCall(Instruction instr, CallInstruction ca
|
||||
}
|
||||
|
||||
/** Holds if `node` represents the output node for `call`. */
|
||||
private predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
operandForFullyConvertedCall(node.asOperand(), call)
|
||||
or
|
||||
instructionForFullyConvertedCall(node.asInstruction(), call)
|
||||
|
||||
@@ -14,6 +14,7 @@ private import DataFlowPrivate
|
||||
private import ModelUtil
|
||||
private import SsaInternals as Ssa
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* The IR dataflow graph consists of the following nodes:
|
||||
@@ -43,11 +44,12 @@ private newtype TIRDataFlowNode =
|
||||
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
|
||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||
} or
|
||||
TRawIndirectOperand(Operand op, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectOperand(op, indirectionIndex)
|
||||
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
|
||||
} or
|
||||
TRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectInstruction(instr, indirectionIndex)
|
||||
TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) {
|
||||
not exists(node.asOperand()) and
|
||||
Ssa::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex)
|
||||
} or
|
||||
TFinalParameterNode(Parameter p, int indirectionIndex) {
|
||||
exists(Ssa::FinalParameterUse use |
|
||||
@@ -917,48 +919,146 @@ Type getTypeImpl(Type t, int indirectionIndex) {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
class RawIndirectOperand extends Node, TRawIndirectOperand {
|
||||
Operand operand;
|
||||
int indirectionIndex;
|
||||
private module RawIndirectNodes {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
private class RawIndirectOperand0 extends Node, TRawIndirectOperand0 {
|
||||
Node0Impl node;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectOperand() { this = TRawIndirectOperand(operand, indirectionIndex) }
|
||||
RawIndirectOperand0() { this = TRawIndirectOperand0(node, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Operand getOperand() { result = operand }
|
||||
/** Gets the underlying instruction. */
|
||||
Operand getOperand() { result = node.asOperand() }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() { result = this.getOperand().getDef().getEnclosingFunction() }
|
||||
override Declaration getFunction() {
|
||||
result = this.getOperand().getDef().getEnclosingFunction()
|
||||
}
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getOperandType(operand, isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getOperandType(this.getOperand(), isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getOperand().getLocation())
|
||||
then result = this.getOperand().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getOperand().getLocation())
|
||||
then result = this.getOperand().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
private class RawIndirectInstruction0 extends Node, TRawIndirectInstruction0 {
|
||||
Node0Impl node;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectInstruction0() { this = TRawIndirectInstruction0(node, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = node.asInstruction() }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getInstructionType(this.getInstruction(), isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getInstruction().getLocation())
|
||||
then result = this.getInstruction().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after a number of loads.
|
||||
*/
|
||||
class RawIndirectOperand extends Node {
|
||||
int indirectionIndex;
|
||||
Operand operand;
|
||||
|
||||
RawIndirectOperand() {
|
||||
exists(Node0Impl node | operand = node.asOperand() |
|
||||
this = TRawIndirectOperand0(node, indirectionIndex)
|
||||
or
|
||||
this = TRawIndirectInstruction0(node, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the operand associated with this node. */
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after a number of loads.
|
||||
*/
|
||||
class RawIndirectInstruction extends Node {
|
||||
int indirectionIndex;
|
||||
Instruction instr;
|
||||
|
||||
RawIndirectInstruction() {
|
||||
exists(Node0Impl node | instr = node.asInstruction() |
|
||||
this = TRawIndirectOperand0(node, indirectionIndex)
|
||||
or
|
||||
this = TRawIndirectInstruction0(node, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the instruction associated with this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
}
|
||||
|
||||
import RawIndirectNodes
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
@@ -1020,48 +1120,6 @@ class UninitializedNode extends Node {
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
class RawIndirectInstruction extends Node, TRawIndirectInstruction {
|
||||
Instruction instr;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectInstruction() { this = TRawIndirectInstruction(instr, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getInstructionType(instr, isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getInstruction().getLocation())
|
||||
then result = this.getInstruction().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
private module GetConvertedResultExpression {
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
@@ -1599,26 +1657,29 @@ private module Cached {
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
|
||||
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
|
||||
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
|
||||
exists(int ind, LoadInstruction load |
|
||||
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
|
||||
nodeHasInstruction(nodeTo, load, ind - 1)
|
||||
)
|
||||
or
|
||||
// If an operand flows to an instruction, then the indirection of
|
||||
// the operand also flows to the indirection of the instruction.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
simpleInstructionLocalFlowStep(operand, instr) and
|
||||
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
or
|
||||
// If there's indirect flow to an operand, then there's also indirect
|
||||
// flow to the operand after applying some pointer arithmetic.
|
||||
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
|
||||
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
|
||||
pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
|
||||
nodeFrom != nodeTo and
|
||||
(
|
||||
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
|
||||
exists(int ind, LoadInstruction load |
|
||||
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
|
||||
nodeHasInstruction(nodeTo, load, ind - 1)
|
||||
)
|
||||
or
|
||||
// If an operand flows to an instruction, then the indirection of
|
||||
// the operand also flows to the indirection of the instruction.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
simpleInstructionLocalFlowStep(operand, instr) and
|
||||
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
or
|
||||
// If there's indirect flow to an operand, then there's also indirect
|
||||
// flow to the operand after applying some pointer arithmetic.
|
||||
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
|
||||
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
|
||||
pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1644,6 +1705,7 @@ private module Cached {
|
||||
private predicate indirectionInstructionFlow(
|
||||
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
|
||||
) {
|
||||
nodeFrom != nodeTo and
|
||||
// If there's flow from an instruction to an operand, then there's also flow from the
|
||||
// indirect instruction to the indirect operand.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
@@ -1696,16 +1758,7 @@ private module Cached {
|
||||
// Reverse flow: data that flows from the definition node back into the indirection returned
|
||||
// by a function. This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
exists(Operand address, int indirectionIndex |
|
||||
nodeHasOperand(nodeTo.(IndirectReturnOutNode), address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddressOperand() = address
|
||||
)
|
||||
or
|
||||
Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex)
|
||||
)
|
||||
reverseFlow(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
|
||||
@@ -1736,6 +1789,39 @@ private module Cached {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reverseFlow(Node nodeFrom, Node nodeTo) {
|
||||
reverseFlowOperand(nodeFrom, nodeTo)
|
||||
or
|
||||
reverseFlowInstruction(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate reverseFlowOperand(Node nodeFrom, IndirectReturnOutNode nodeTo) {
|
||||
exists(Operand address, int indirectionIndex |
|
||||
nodeHasOperand(nodeTo, address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddressOperand() = address
|
||||
)
|
||||
or
|
||||
// We also want a write coming out of an `OutNode` to flow `nodeTo`.
|
||||
// This is different from `reverseFlowInstruction` since `nodeFrom` can never
|
||||
// be an `OutNode` when it's defined by an instruction.
|
||||
Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reverseFlowInstruction(Node nodeFrom, IndirectReturnOutNode nodeTo) {
|
||||
exists(Instruction address, int indirectionIndex |
|
||||
nodeHasInstruction(nodeTo, address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddress() = address
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -2215,33 +2301,41 @@ module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardCheck
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
* A unit class for adding additional call steps.
|
||||
*
|
||||
* A guard that validates some instruction.
|
||||
* Extend this class to add additional call steps to the data flow graph.
|
||||
*
|
||||
* 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.
|
||||
* For example, if the following subclass is added:
|
||||
* ```ql
|
||||
* class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
|
||||
* override Function viableTarget(Call call) {
|
||||
* call.getTarget().hasName("f") and
|
||||
* result.hasName("g")
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* then flow from `source()` to `x` in `sink(x)` is reported in the following example:
|
||||
* ```cpp
|
||||
* void sink(int);
|
||||
* int source();
|
||||
* void f(int);
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
* void g(int x) {
|
||||
* sink(x);
|
||||
* }
|
||||
*
|
||||
* void test() {
|
||||
* int x = source();
|
||||
* f(x);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note: To prevent reevaluation of cached dataflow-related predicates any
|
||||
* subclass of `AdditionalCallTarget` must be imported in all dataflow queries.
|
||||
*/
|
||||
deprecated class BarrierGuard extends IRGuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
|
||||
predicate checksInstr(Instruction instr, boolean b) { none() }
|
||||
|
||||
/** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */
|
||||
predicate checks(Expr e, boolean b) { none() }
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
exists(ValueNumber value, boolean edge |
|
||||
(
|
||||
this.checksInstr(value.getAnInstruction(), edge)
|
||||
or
|
||||
this.checks(value.getAnInstruction().getConvertedResultExpression(), edge)
|
||||
) and
|
||||
result.asInstruction() = value.getAnInstruction() and
|
||||
this.controls(result.asInstruction().getBlock(), edge)
|
||||
)
|
||||
}
|
||||
class AdditionalCallTarget extends Unit {
|
||||
/**
|
||||
* Gets a viable target for `call`.
|
||||
*/
|
||||
abstract DataFlowCallable viableTarget(Call call);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
@@ -30,26 +31,35 @@ DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node that represents the output of `call` with kind `output` at
|
||||
* indirection index `indirectionIndex`.
|
||||
*/
|
||||
private Node callOutputWithIndirectionIndex(
|
||||
CallInstruction call, FunctionOutput output, int indirectionIndex
|
||||
) {
|
||||
// The return value
|
||||
simpleOutNode(result, call) and
|
||||
output.isReturnValue() and
|
||||
indirectionIndex = 0
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(int index |
|
||||
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
|
||||
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex - 1 and
|
||||
result.(IndirectArgumentOutNode).getCallInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(index, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
result = getIndirectReturnOutNode(call, indirectionIndex) and
|
||||
output.isReturnValueDeref(indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
Node callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result.asInstruction() = call and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(int index, int indirectionIndex |
|
||||
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
|
||||
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and
|
||||
result.(IndirectArgumentOutNode).getCallInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
output.isReturnValueDeref(ind)
|
||||
)
|
||||
result = callOutputWithIndirectionIndex(call, output, _)
|
||||
}
|
||||
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) {
|
||||
@@ -75,19 +85,15 @@ private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int
|
||||
*/
|
||||
bindingset[d]
|
||||
Node callOutput(CallInstruction call, FunctionOutput output, int d) {
|
||||
exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 |
|
||||
exists(DataFlow::Node n, int indirectionIndex |
|
||||
n = callOutputWithIndirectionIndex(call, output, indirectionIndex) and d > 0
|
||||
|
|
||||
// The return value
|
||||
result = getIndirectReturnOutNode(n.asInstruction(), d)
|
||||
result = callOutputWithIndirectionIndex(call, output, indirectionIndex + d)
|
||||
or
|
||||
// If there isn't an indirect out node for the call with indirection `d` then
|
||||
// we conflate this with the underlying `CallInstruction`.
|
||||
not exists(getIndirectReturnOutNode(call, d)) and
|
||||
n.asInstruction() = result.asInstruction()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and
|
||||
Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
not exists(getIndirectReturnOutNode(call, indirectionIndex + d)) and
|
||||
n = result
|
||||
)
|
||||
}
|
||||
|
||||
@@ -374,6 +374,8 @@ module ProductFlow {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier1(node, state) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { Config::isBarrier1(node) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut1(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
@@ -408,6 +410,8 @@ module ProductFlow {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier2(node, state) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { Config::isBarrier2(node) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut2(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
|
||||
@@ -645,12 +645,24 @@ private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` flows to `nodeTo` because there is `def-use` or
|
||||
* `use-use` flow from `defOrUse` to `use`.
|
||||
*
|
||||
* `uncertain` is `true` if the `defOrUse` is an uncertain definition.
|
||||
*/
|
||||
private predicate localSsaFlow(
|
||||
SsaDefOrUse defOrUse, Node nodeFrom, UseOrPhi use, Node nodeTo, boolean uncertain
|
||||
) {
|
||||
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
|
||||
adjacentDefRead(defOrUse, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
}
|
||||
|
||||
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
|
||||
exists(UseOrPhi use |
|
||||
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
|
||||
adjacentDefRead(defOrUse, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
localSsaFlow(defOrUse, nodeFrom, use, nodeTo, uncertain)
|
||||
or
|
||||
// Initial global variable value to a first use
|
||||
nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and
|
||||
@@ -728,15 +740,62 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there is def-use or use-use flow from `pun` to `nodeTo`. */
|
||||
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
|
||||
exists(UseOrPhi use, Node preUpdate |
|
||||
/**
|
||||
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
|
||||
*/
|
||||
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
|
||||
exists(UseOrPhi use |
|
||||
adjustForPointerArith(pun, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
useToNode(use, n)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepUntilNotInCall(DataFlowCall call, Node n1, Node n2) {
|
||||
isArgumentOfCallable(call, n1) and
|
||||
exists(Node mid | localSsaFlow(_, n1, _, mid, _) |
|
||||
isArgumentOfCallable(call, mid) and
|
||||
stepUntilNotInCall(call, mid, n2)
|
||||
or
|
||||
not isArgumentOfCallable(call, mid) and
|
||||
mid = n2
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[n1, n2]
|
||||
pragma[inline_late]
|
||||
private predicate isArgumentOfSameCall(DataFlowCall call, Node n1, Node n2) {
|
||||
isArgumentOfCallable(call, n1) and isArgumentOfCallable(call, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is def-use or use-use flow from `pun` to `nodeTo`.
|
||||
*
|
||||
* Note: This is more complex than it sounds. Consider a call such as:
|
||||
* ```cpp
|
||||
* write_first_argument(x, x);
|
||||
* sink(x);
|
||||
* ```
|
||||
* Assume flow comes out of the first argument to `write_first_argument`. We
|
||||
* don't want flow to go to the `x` that's also an argument to
|
||||
* `write_first_argument` (because we just flowed out of that function, and we
|
||||
* don't want to flow back into it again).
|
||||
*
|
||||
* We do, however, want flow from the output argument to `x` on the next line, and
|
||||
* similarly we want flow from the second argument of `write_first_argument` to `x`
|
||||
* on the next line.
|
||||
*/
|
||||
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
|
||||
exists(Node preUpdate, Node mid |
|
||||
preUpdate = pun.getPreUpdateNode() and
|
||||
not exists(DataFlowCall call |
|
||||
isArgumentOfCallable(call, preUpdate) and isArgumentOfCallable(call, nodeTo)
|
||||
postUpdateNodeToFirstUse(pun, mid)
|
||||
|
|
||||
exists(DataFlowCall call |
|
||||
isArgumentOfSameCall(call, preUpdate, mid) and
|
||||
stepUntilNotInCall(call, mid, nodeTo)
|
||||
)
|
||||
or
|
||||
not isArgumentOfSameCall(_, preUpdate, mid) and
|
||||
nodeTo = mid
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ private class PointerWrapperTypeIndirection extends Indirection instanceof Point
|
||||
override predicate isAdditionalDereference(Instruction deref, Operand address) {
|
||||
exists(CallInstruction call |
|
||||
operandForFullyConvertedCall(getAUse(deref), call) and
|
||||
this = call.getStaticCallTarget().getClassAndName("operator*") and
|
||||
this = call.getStaticCallTarget().getClassAndName(["operator*", "operator->", "get"]) and
|
||||
address = call.getThisArgumentOperand()
|
||||
)
|
||||
}
|
||||
@@ -815,7 +815,7 @@ private module Cached {
|
||||
) {
|
||||
indirectionIndex = [1 .. countIndirectionsForCppType(getResultLanguageType(instr))] and
|
||||
exists(Instruction load, Operand address |
|
||||
address.getDef() = instr and
|
||||
address = unique( | | getAUse(instr)) and
|
||||
isDereference(load, address, false) and
|
||||
instrRepr = load and
|
||||
indirectionIndexRepr = indirectionIndex - 1
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -423,7 +423,12 @@ private module CachedForDebugging {
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instruction, int key1, int key2) {
|
||||
key1 = getInstructionTranslatedElement(instruction).getId() and
|
||||
getInstructionTag(instruction) =
|
||||
getInstructionTag(instruction) = tagByRank(key2)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private InstructionTag tagByRank(int key2) {
|
||||
result =
|
||||
rank[key2](InstructionTag tag, string tagId |
|
||||
tagId = getInstructionTagId(tag)
|
||||
|
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.internal.ExtractorVersion
|
||||
private import semmle.code.cpp.ir.IRConfiguration
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
@@ -361,6 +362,12 @@ predicate ignoreLoad(Expr expr) {
|
||||
or
|
||||
expr instanceof FunctionAccess
|
||||
or
|
||||
// The load is duplicated from the operand.
|
||||
isExtractorFrontendVersion65OrHigher() and expr instanceof ParenthesisExpr
|
||||
or
|
||||
// The load is duplicated from the right operand.
|
||||
isExtractorFrontendVersion65OrHigher() and expr instanceof CommaExpr
|
||||
or
|
||||
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
|
||||
instanceof FunctionPointerType
|
||||
or
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.internal.ExtractorVersion
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
@@ -649,7 +650,9 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
|
||||
override PrefixCrementOperation expr;
|
||||
|
||||
override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
// The following distinction is needed to work around extractor limitations
|
||||
// in old versions of the extractor.
|
||||
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
// If this is C, then the result of a prefix crement is a prvalue for the
|
||||
// new value assigned to the operand. If this is C++, then the result is
|
||||
@@ -1504,7 +1507,9 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
final override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
// The following distinction is needed to work around extractor limitations
|
||||
// in old versions of the extractor.
|
||||
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
// If this is C, then the result of an assignment is a prvalue for the new
|
||||
// value assigned to the left operand. If this is C++, then the result is
|
||||
@@ -1642,7 +1647,9 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
final override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
// The following distinction is needed to work around extractor limitations
|
||||
// in old versions of the extractor.
|
||||
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
// If this is C, then the result of an assignment is a prvalue for the new
|
||||
// value assigned to the left operand. If this is C++, then the result is
|
||||
@@ -2191,8 +2198,16 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
not this.elseIsVoid() and tag = ConditionValueFalseStoreTag()
|
||||
) and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = this.getResultType()
|
||||
if isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
resultType = this.getResultType()
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
else resultType = this.getResultType()
|
||||
or
|
||||
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = this.getResultType()
|
||||
@@ -2222,8 +2237,16 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
)
|
||||
or
|
||||
tag = ConditionValueResultTempAddressTag() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
if isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
else result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
or
|
||||
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
@@ -2252,18 +2275,24 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
result = this.getElse().getResult()
|
||||
)
|
||||
or
|
||||
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
)
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
not this.resultIsVoid() and
|
||||
tag = ConditionValueTempVar() and
|
||||
type = this.getResultType()
|
||||
if isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
type = this.getResultType()
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
else type = this.getResultType()
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -2278,7 +2307,14 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
final override Instruction getResult() {
|
||||
not this.resultIsVoid() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
if isExtractorFrontendVersion65OrHigher()
|
||||
then
|
||||
expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
or
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
else result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
@@ -3238,10 +3274,18 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
|
||||
expr instanceof AssignExpr
|
||||
or
|
||||
expr instanceof AssignOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
(
|
||||
not expr.isPRValueCategory() // is C++
|
||||
or
|
||||
isExtractorFrontendVersion65OrHigher()
|
||||
)
|
||||
or
|
||||
expr instanceof PrefixCrementOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
(
|
||||
not expr.isPRValueCategory() // is C++
|
||||
or
|
||||
isExtractorFrontendVersion65OrHigher()
|
||||
)
|
||||
or
|
||||
// Because the load is on the `e` in `e++`.
|
||||
expr instanceof PostfixCrementOperation
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ private import implementations.Strtok
|
||||
private import implementations.Strset
|
||||
private import implementations.Strcrement
|
||||
private import implementations.Strnextc
|
||||
private import implementations.Strtol
|
||||
private import implementations.StdContainer
|
||||
private import implementations.StdPair
|
||||
private import implementations.StdMap
|
||||
@@ -34,6 +35,7 @@ private import implementations.Accept
|
||||
private import implementations.Poll
|
||||
private import implementations.Select
|
||||
private import implementations.MySql
|
||||
private import implementations.ODBC
|
||||
private import implementations.SqLite3
|
||||
private import implementations.PostgreSql
|
||||
private import implementations.System
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* An allocation function (such as `malloc`) that has an argument for the size
|
||||
@@ -121,7 +122,7 @@ private class CallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `realloc`) that has an argument for the size
|
||||
* in bytes, and an argument for an existing pointer that is to be reallocated.
|
||||
*/
|
||||
private class ReallocAllocationFunction extends AllocationFunction {
|
||||
private class ReallocAllocationFunction extends AllocationFunction, TaintFunction {
|
||||
int sizeArg;
|
||||
int reallocArg;
|
||||
|
||||
@@ -151,6 +152,10 @@ private class ReallocAllocationFunction extends AllocationFunction {
|
||||
override int getSizeArg() { result = sizeArg }
|
||||
|
||||
override int getReallocPtrArg() { result = reallocArg }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.getReallocPtrArg()) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,18 +9,17 @@ import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
private class MemsetFunctionModel extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
MemsetFunction() {
|
||||
MemsetFunctionModel() {
|
||||
this.hasGlobalOrStdOrBslName("memset")
|
||||
or
|
||||
this.hasGlobalOrStdName("wmemset")
|
||||
or
|
||||
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
|
||||
this.hasGlobalName([
|
||||
bzero(), "__builtin_memset", "__builtin_memset_chk", "RtlZeroMemory", "RtlSecureZeroMemory"
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
@@ -60,3 +59,8 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
|
||||
}
|
||||
|
||||
private string bzero() { result = ["bzero", "explicit_bzero"] }
|
||||
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
class MemsetFunction extends Function instanceof MemsetFunctionModel { }
|
||||
|
||||
28
cpp/ql/lib/semmle/code/cpp/models/implementations/ODBC.qll
Normal file
28
cpp/ql/lib/semmle/code/cpp/models/implementations/ODBC.qll
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the ODBC C/C++ API.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.models.interfaces.Sql
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
/**
|
||||
* The `SQLExecDirect`, and `SQLPrepare` from the ODBC C/C++ API:
|
||||
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlexecdirect-function?view=sql-server-ver16
|
||||
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlprepare-function?view=sql-server-ver16
|
||||
*
|
||||
* Note, `SQLExecute` is not included because it operates on a SQLHSTMT type, not a string.
|
||||
* The SQLHSTMT parameter for `SQLExecute` is set through a `SQLPrepare`, which is modeled.
|
||||
* The other source of input to a `SQLExecute` is via a `SQLBindParameter`, which sanitizes user input,
|
||||
* and would be considered a barrier to SQL injection.
|
||||
*/
|
||||
private class ODBCExecutionFunction extends SqlExecutionFunction {
|
||||
ODBCExecutionFunction() { this.hasGlobalName(["SQLExecDirect", "SQLPrepare"]) }
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
|
||||
}
|
||||
// NOTE: no need to define a barrier explicitly.
|
||||
// `SQLBindParameter` is the typical means for sanitizing user input.
|
||||
// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver16
|
||||
// First a query is establisehed via `SQLPrepare`, then parameters are bound via `SQLBindParameter`, before
|
||||
// the query is executed via `SQLExecute`. We are not modeling SQLExecute, so we do not need to model SQLBindParameter.
|
||||
@@ -13,7 +13,7 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
PureStrFunction() {
|
||||
this.hasGlobalOrStdOrBslName([
|
||||
atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr",
|
||||
"strspn", strtol(), strrev(), strcmp(), strlwr(), strupr()
|
||||
"strspn", strrev(), strcmp(), strlwr(), strupr()
|
||||
])
|
||||
}
|
||||
|
||||
@@ -70,8 +70,6 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
|
||||
private string atoi() { result = ["atof", "atoi", "atol", "atoll"] }
|
||||
|
||||
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
|
||||
|
||||
private string strlwr() {
|
||||
result = ["_strlwr", "_wcslwr", "_mbslwr", "_strlwr_l", "_wcslwr_l", "_mbslwr_l"]
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ private class Strtok extends ArrayFunction, AliasFunction, TaintFunction, SideEf
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
|
||||
54
cpp/ql/lib/semmle/code/cpp/models/implementations/Strtol.qll
Normal file
54
cpp/ql/lib/semmle/code/cpp/models/implementations/Strtol.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
|
||||
|
||||
/**
|
||||
* The standard function `strtol` and its assorted variants
|
||||
*/
|
||||
private class Strtol extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
Strtol() { this.hasGlobalOrStdOrBslName(strtol()) }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
// All the functions given by `strtol()` takes a `const char*` input as the first parameter
|
||||
bufParam = 0
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isParameter(0)
|
||||
or
|
||||
input.isParameterDeref(0)
|
||||
) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameter(0) and
|
||||
output.isParameterDeref(1)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int i) {
|
||||
// Parameter 0 does escape into parameter 1.
|
||||
i = 1
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int i) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and
|
||||
buffer = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and buffer = false and mustWrite = false
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ private import RangeAnalysisUtils
|
||||
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
|
||||
* This class provides the range-analysis SSA logic.
|
||||
*/
|
||||
library class RangeSsa extends SsaHelper {
|
||||
class RangeSsa extends SsaHelper {
|
||||
RangeSsa() { this = 1 }
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,18 @@ class SemLocation instanceof Location {
|
||||
*/
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location starts. */
|
||||
int getStartLine() { result = super.getStartLine() }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location starts. */
|
||||
int getStartColumn() { result = super.getStartColumn() }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location ends. */
|
||||
int getEndLine() { result = super.getEndLine() }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location ends. */
|
||||
int getEndColumn() { result = super.getEndColumn() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
|
||||
@@ -23,6 +23,8 @@ class SemSsaExplicitUpdate extends SemSsaVariable {
|
||||
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
|
||||
|
||||
final SemExpr getSourceExpr() { result = sourceExpr }
|
||||
|
||||
final SemExpr getDefiningExpr() { result = sourceExpr }
|
||||
}
|
||||
|
||||
class SemSsaPhiNode extends SemSsaVariable {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisImpl
|
||||
private import codeql.rangeanalysis.RangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExpr
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticType
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
@@ -20,7 +22,7 @@ module FloatDelta implements DeltaSig {
|
||||
Delta fromFloat(float f) { result = f }
|
||||
}
|
||||
|
||||
module FloatOverflow implements OverflowSig<FloatDelta> {
|
||||
module FloatOverflow implements OverflowSig<Sem, FloatDelta> {
|
||||
predicate semExprDoesNotOverflow(boolean positively, SemExpr expr) {
|
||||
exists(float lb, float ub, float delta |
|
||||
typeBounds(expr.getSemType(), lb, ub) and
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user