diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json
index ac468d9838..b7d2d5604c 100644
--- a/.codeqlmanifest.json
+++ b/.codeqlmanifest.json
@@ -1 +1,9 @@
-{ "provide": [ "codeql_modules/*/.codeqlmanifest.json", "cpp/.codeqlmanifest.json", "c/.codeqlmanifest.json"] }
+{
+ "provide": [
+ "cpp/*/src/qlpack.yml",
+ "cpp/*/test/qlpack.yml",
+ "c/*/src/qlpack.yml",
+ "c/*/test/qlpack.yml",
+ "scripts/generate_modules/queries/qlpack.yml"
+ ]
+}
\ No newline at end of file
diff --git a/.github/actions/check-permissions/action.yml b/.github/actions/check-permissions/action.yml
new file mode 100644
index 0000000000..b47466d080
--- /dev/null
+++ b/.github/actions/check-permissions/action.yml
@@ -0,0 +1,49 @@
+name: Check current actor permissions
+description: |
+ Checks whether the current actor has the specified permssions
+inputs:
+ minimum-permission:
+ description: |
+ The minimum required permission. One of: read, write, admin
+ required: true
+outputs:
+ has-permission:
+ description: "Whether the actor had the minimum required permission"
+ value: ${{ steps.check-permission.outputs.has-permission }}
+
+runs:
+ using: composite
+ steps:
+ - uses: actions/github-script@v7
+ id: check-permission
+ env:
+ INPUT_MINIMUM-PERMISSION: ${{ inputs.minimum-permission }}
+ with:
+ script: |
+ // Valid permissions are none, read, write, admin (legacy base permissions)
+ const permissionsRanking = ["none", "read", "write", "admin"];
+
+ // Note: core.getInput doesn't work by default in a composite action - in this case
+ // it would try to fetch the input to the github-script instead of the action
+ // itself. Instead, we set the appropriate magic env var with the actions input.
+ // See: https://github.com/actions/runner/issues/665
+ const minimumPermission = core.getInput('minimum-permission');
+ if (!permissionsRanking.includes(minimumPermission)) {
+ core.setFailed(`Invalid minimum permission: ${minimumPermission}`);
+ return;
+ }
+
+ const { data : { permission : actorPermission } } = await github.rest.repos.getCollaboratorPermissionLevel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ username: context.actor
+ });
+
+ // Confirm whether the actor permission is at least the selected permission
+ const hasPermission = permissionsRanking.indexOf(minimumPermission) <= permissionsRanking.indexOf(actorPermission) ? "1" : "";
+ core.setOutput('has-permission', hasPermission);
+ if (!hasPermission) {
+ core.info(`Current actor (${context.actor}) does not have the minimum required permission '${minimumPermission}' (has '${actorPermission}')`);
+ } else {
+ core.info(`Current actor (${context.actor}) has the minimum required permission '${minimumPermission}' (has '${actorPermission}')`);
+ }
diff --git a/.github/actions/install-codeql-packs/action.yml b/.github/actions/install-codeql-packs/action.yml
new file mode 100644
index 0000000000..2e6d5f1a2e
--- /dev/null
+++ b/.github/actions/install-codeql-packs/action.yml
@@ -0,0 +1,25 @@
+name: Install CodeQL library pack dependencies
+description: |
+ Downloads any necessary CodeQL library packs needed by packs in the repo.
+inputs:
+ cli_path:
+ description: |
+ The path to the CodeQL CLI directory.
+ required: false
+
+ mode:
+ description: |
+ The `--mode` option to `codeql pack install`.
+ required: true
+ default: verify
+
+runs:
+ using: composite
+ steps:
+ - name: Install CodeQL library packs
+ shell: bash
+ env:
+ CODEQL_CLI: ${{ inputs.cli_path }}
+ run: |
+ PATH=$PATH:$CODEQL_CLI
+ python scripts/install-packs.py --mode ${{ inputs.mode }}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..73f11c1f47
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,8 @@
+version: 2
+updates:
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ # Check for updates to GitHub Actions every week
+ interval: "weekly"
\ No newline at end of file
diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml
deleted file mode 100644
index dad7061670..0000000000
--- a/.github/workflows/bump-version.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: 📦 Bump Workflow
-
-on:
- workflow_dispatch:
- inputs:
- new_version:
- description: |
- The version to update to (eg: 2.6.0 or 2.6.0-dev do not include `v`).
- required: true
-jobs:
-
- apply-version-bump:
- runs-on: ubuntu-22.04
- name: Apply Version Bump
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Apply Bump
- shell: bash
- run: |
- bash ./scripts/bump_version.sh ${{ github.event.inputs.new_version }}
-
- - name: Create Pull Request
- uses: peter-evans/create-pull-request@v4
- with:
- title: "Release Engineering: Version bump to ${{ github.event.inputs.new_version }}."
- body: "This PR updates codeql-coding-standards to version ${{ github.event.inputs.new_version }}."
- commit-message: "Version bump to ${{ github.event.inputs.new_version }}."
- team-reviewers: github/codeql-coding-standards
- delete-branch: true
- branch: "automation/version-bump-${{ github.event.inputs.new_version }}"
diff --git a/.github/workflows/code-scanning-pack-gen.yml b/.github/workflows/code-scanning-pack-gen.yml
index 8c9fa46725..678b3be403 100644
--- a/.github/workflows/code-scanning-pack-gen.yml
+++ b/.github/workflows/code-scanning-pack-gen.yml
@@ -1,22 +1,24 @@
name: Code Scanning Query Pack Generation
on:
+ merge_group:
+ types: [checks_requested]
pull_request:
branches:
- main
- - "rc/**"
- next
-
+ - "rc/**"
push:
branches:
- main
- - "rc/**"
- next
+ - "rc/**"
env:
XARGS_MAX_PROCS: 4
jobs:
+
prepare-code-scanning-pack-matrix:
name: Prepare CodeQL Code Scanning pack matrix
runs-on: ubuntu-22.04
@@ -24,28 +26,27 @@ jobs:
matrix: ${{ steps.export-code-scanning-pack-matrix.outputs.matrix }}
steps:
- name: Checkout repository
- uses: actions/checkout@v2
-
+ uses: actions/checkout@v4
- name: Export Code Scanning pack matrix
id: export-code-scanning-pack-matrix
run: |
- echo "::set-output name=matrix::$(
+ echo "matrix=$(
jq --compact-output '.supported_environment | {include: .}' supported_codeql_configs.json
- )"
+ )" >> $GITHUB_OUTPUT
create-code-scanning-pack:
name: Create Code Scanning pack
needs: prepare-code-scanning-pack-matrix
- runs-on: ubuntu-20.04-xl
+ runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare-code-scanning-pack-matrix.outputs.matrix) }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Cache CodeQL
id: cache-codeql
- uses: actions/cache@v2.1.3
+ uses: actions/cache@v4
with:
path: ${{ github.workspace }}/codeql_home
key: codeql-home-${{ matrix.os }}-${{ matrix.codeql_cli }}-${{ matrix.codeql_standard_library }}
@@ -59,18 +60,37 @@ jobs:
codeql-home: ${{ github.workspace }}/codeql_home
add-to-path: false
+ - name: Install CodeQL packs
+ uses: ./.github/actions/install-codeql-packs
+ with:
+ cli_path: ${{ github.workspace }}/codeql_home/codeql
+
+ - name: Determine ref for external help files
+ id: determine-ref
+ run: |
+ if [[ $GITHUB_EVENT_NAME == "pull_request" ]]; then
+ EXTERNAL_HELP_REF="${{ github.event.pull_request.base.ref }}"
+ elif [[ $GITHUB_EVENT_NAME == "merge_group" ]]; then
+ EXTERNAL_HELP_REF="${{ github.event.merge_group.base_ref }}"
+ else
+ EXTERNAL_HELP_REF="$GITHUB_REF"
+ fi
+ echo "EXTERNAL_HELP_REF=$EXTERNAL_HELP_REF" >> "$GITHUB_ENV"
+ echo "Using ref $EXTERNAL_HELP_REF for external help files."
+
- name: Checkout external help files
- continue-on-error: true
id: checkout-external-help-files
- uses: actions/checkout@v2
+ # PRs from forks and dependabot do not have access to an appropriate token for cloning the help files repos
+ if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
+ uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.CODEQL_CODING_STANDARDS_HELP_KEY }}
repository: "github/codeql-coding-standards-help"
- ref: ${{ github.head_ref }}
+ ref: ${{ env.EXTERNAL_HELP_REF }}
path: external-help-files
- name: Include external help files
- if: ${{ steps.checkout-external-help-files.outcome == 'success' }}
+ if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]'&& steps.checkout-external-help-files.outcome == 'success' }}
run: |
pushd external-help-files
find . -name '*.md' -exec rsync -av --relative {} "$GITHUB_WORKSPACE" \;
@@ -81,15 +101,36 @@ jobs:
CODEQL_HOME: ${{ github.workspace }}/codeql_home
run: |
PATH=$PATH:$CODEQL_HOME/codeql
-
- codeql query compile --search-path cpp --threads 0 cpp
- codeql query compile --search-path c --search-path cpp --threads 0 c
+ # Precompile all queries, and use a compilation cache larger than default
+ # to ensure we cache all the queries for later steps
+ codeql query compile --precompile --threads 0 --compilation-cache-size=1024 cpp c
cd ..
- zip -r codeql-coding-standards/code-scanning-cpp-query-pack.zip codeql-coding-standards/c/ codeql-coding-standards/cpp/ codeql-coding-standards/.codeqlmanifest.json codeql-coding-standards/supported_codeql_configs.json codeql-coding-standards/scripts/configuration codeql-coding-standards/scripts/reports codeql-coding-standards/scripts/shared codeql-coding-standards/scripts/guideline_recategorization codeql-coding-standards/scripts/shared codeql-coding-standards/scripts/schemas
+ zip -r codeql-coding-standards/code-scanning-cpp-query-pack.zip codeql-coding-standards/c/ codeql-coding-standards/cpp/ codeql-coding-standards/.codeqlmanifest.json codeql-coding-standards/supported_codeql_configs.json codeql-coding-standards/scripts/configuration codeql-coding-standards/scripts/reports codeql-coding-standards/scripts/shared codeql-coding-standards/scripts/guideline_recategorization codeql-coding-standards/schemas
- name: Upload GHAS Query Pack
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: code-scanning-cpp-query-pack.zip
path: code-scanning-cpp-query-pack.zip
+
+ - name: Create qlpack bundles
+ env:
+ CODEQL_HOME: ${{ github.workspace }}/codeql_home
+ run: |
+ PATH=$PATH:$CODEQL_HOME/codeql
+
+ codeql pack bundle --output=common-cpp-coding-standards.tgz cpp/common/src
+ codeql pack bundle --output=common-c-coding-standards.tgz c/common/src
+ codeql pack bundle --output=misra-c-coding-standards.tgz c/misra/src
+ codeql pack bundle --output=cert-c-coding-standards.tgz c/cert/src
+ codeql pack bundle --output=cert-cpp-coding-standards.tgz cpp/cert/src
+ codeql pack bundle --output=autosar-cpp-coding-standards.tgz cpp/autosar/src
+ codeql pack bundle --output=misra-cpp-coding-standards.tgz cpp/misra/src
+ codeql pack bundle --output=report-coding-standards.tgz cpp/report/src
+
+ - name: Upload qlpack bundles
+ uses: actions/upload-artifact@v4
+ with:
+ name: coding-standards-codeql-packs
+ path: '*-coding-standards.tgz'
\ No newline at end of file
diff --git a/.github/workflows/codeql_unit_tests.yml b/.github/workflows/codeql_unit_tests.yml
index b23bc0cc19..2fc28fc900 100644
--- a/.github/workflows/codeql_unit_tests.yml
+++ b/.github/workflows/codeql_unit_tests.yml
@@ -1,17 +1,21 @@
name: CodeQL Unit Testing
on:
+ merge_group:
+ types: [checks_requested]
push:
branches:
- main
- - "rc/**"
- next
+ - "rc/**"
pull_request:
branches:
- - "**"
- workflow_dispatch:
+ - main
+ - next
+ - "rc/**"
jobs:
+
prepare-unit-test-matrix:
name: Prepare CodeQL unit test matrix
runs-on: ubuntu-22.04
@@ -19,16 +23,16 @@ jobs:
matrix: ${{ steps.export-unit-test-matrix.outputs.matrix }}
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Export unit test matrix
id: export-unit-test-matrix
run: |
echo "Merging Result:"
python scripts/create_language_matrix.py
- echo "::set-output name=matrix::$(
+ echo "matrix=$(
python scripts/create_language_matrix.py | \
- jq --compact-output 'map([.+{os: "ubuntu-20.04-xl", codeql_standard_library_ident : .codeql_standard_library | sub("\/"; "_")}]) | flatten | {include: .}')"
+ jq --compact-output 'map([.+{os: "ubuntu-latest-xl", codeql_standard_library_ident : .codeql_standard_library | sub("\/"; "_")}]) | flatten | {include: .}')" >> $GITHUB_OUTPUT
run-test-suites:
name: Run unit tests
@@ -38,19 +42,22 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare-unit-test-matrix.outputs.matrix) }}
-
+
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
+ - name: Install Python dependencies
+ run: pip install -r scripts/requirements.txt
+
- name: Cache CodeQL
id: cache-codeql
- uses: actions/cache@v2.1.3
+ uses: actions/cache@v4
with:
# A list of files, directories, and wildcard patterns to cache and restore
path: ${{github.workspace}}/codeql_home
@@ -66,11 +73,15 @@ jobs:
codeql-home: ${{ github.workspace }}/codeql_home
add-to-path: false
+ - name: Install CodeQL packs
+ uses: ./.github/actions/install-codeql-packs
+ with:
+ cli_path: ${{ github.workspace }}/codeql_home/codeql
+
- name: Pre-Compile Queries
id: pre-compile-queries
run: |
- ${{ github.workspace }}/codeql_home/codeql/codeql query compile --search-path cpp --threads 0 cpp
- ${{ github.workspace }}/codeql_home/codeql/codeql query compile --search-path c --search-path cpp --threads 0 c
+ ${{ github.workspace }}/codeql_home/codeql/codeql query compile --threads 0 ${{ matrix.language }}
- name: Run test suites
@@ -93,7 +104,7 @@ jobs:
def print_error(fmt, *args):
print(f"::error::{fmt}", *args)
-
+
def print_error_and_fail(fmt, *args):
print_error(fmt, args)
sys.exit(1)
@@ -122,18 +133,11 @@ jobs:
os.makedirs(os.path.dirname(test_report_path), exist_ok=True)
test_report_file = open(test_report_path, 'w')
files_to_close.append(test_report_file)
- if "${{ matrix.language }}".casefold() == "c".casefold():
- # c tests require cpp -- but we don't want c things on the cpp
- # path in case of design errors.
- cpp_language_root = Path(workspace, 'cpp')
- procs.append(subprocess.Popen([codeql_bin, "test", "run", "--failing-exitcode=122", f"--slice={slice}/{num_slices}", "--ram=2048", "--format=json", f'--search-path={cpp_language_root}', f'--search-path={language_root}', *test_roots], stdout=test_report_file, stderr=subprocess.PIPE))
- else:
- procs.append(subprocess.Popen([codeql_bin, "test", "run", "--failing-exitcode=122", f"--slice={slice}/{num_slices}", "--ram=2048", "--format=json", f'--search-path={language_root}', f'--search-path={language_root}', *test_roots], stdout=test_report_file, stderr=subprocess.PIPE))
+ procs.append(subprocess.Popen([codeql_bin, "test", "run", "--failing-exitcode=122", f"--slice={slice}/{num_slices}", "--ram=2048", "--format=json", *test_roots], stdout=test_report_file, stderr=subprocess.PIPE))
for p in procs:
- p.wait()
+ _, err = p.communicate()
if p.returncode != 0:
- _, err = p.communicate()
if p.returncode == 122:
# Failed because a test case failed, so just print the regular output.
# This will allow us to proceed to validate-test-results, which will fail if
@@ -147,7 +151,7 @@ jobs:
file.close()
- name: Upload test results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: ${{ matrix.language }}-test-results-${{ runner.os }}-${{ matrix.codeql_cli }}-${{ matrix.codeql_standard_library_ident }}
path: |
@@ -156,11 +160,18 @@ jobs:
validate-test-results:
name: Validate test results
- needs: [run-test-suites]
+ if: ${{ always() }}
+ needs: run-test-suites
runs-on: ubuntu-22.04
steps:
+ - name: Check if run-test-suites job failed to complete, if so fail
+ if: ${{ needs.run-test-suites.result == 'failure' }}
+ uses: actions/github-script@v7
+ with:
+ script: |
+ core.setFailed('Test run job failed')
- name: Collect test results
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
- name: Validate test results
run: |
diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml
deleted file mode 100644
index 932c1f2abb..0000000000
--- a/.github/workflows/create-draft-release.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: Create draft release
-
-on:
- workflow_dispatch:
- inputs:
- release_version_tag:
- description: |
- The tag for the new draft release, e.g. v0.5.1.
- required: true
- codeql_analysis_threads:
- description: |
- Number of threads to evaluate queries
- required: true
- default: 6
- aws_ec2_instance_type:
- description: |
- Recommended specs: 8+ vCPU 32+ GB RAM (e.g. t2.2xlarge, r5.2xlarge)
- required: true
- default: r5.2xlarge
-
-jobs:
- create-draft-release:
- name: Create draft release
- runs-on: ubuntu-22.04
- env:
- # AWS CONFIGURATION
- AWS_EC2_INSTANCE_TYPE: ${{ github.event.inputs.aws_ec2_instance_type }}
-
- # CODEQL CONFIGURATION
- CODEQL_ANALYSIS_THREADS: ${{ github.event.inputs.codeql_analysis_threads }}
-
- # INTEGRATION TESTING CONFIGURATION
- INTEGRATION_TESTING_ACCESS_TOKEN: ${{ secrets.INTEGRATION_TESTING_ACCESS_TOKEN }}
- WORKFLOW_ID: 11846210
-
- # RELEASE VERSION TAG
- RELEASE_VERSION_TAG: ${{ github.event.inputs.release_version_tag }}
- steps:
- - name: Checkout
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- - name: Install Python
- uses: actions/setup-python@v4
- with:
- python-version: "3.9"
-
- - name: Install generate_release_notes.py dependencies
- run: pip install -r scripts/requirements.txt
-
- - name: Create draft release
- run: |
- scripts/release/create_draft_release.sh ${GITHUB_REF#refs/heads/} "$RELEASE_VERSION_TAG"
- env:
- GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/dispatch-matrix-test-on-comment.yml b/.github/workflows/dispatch-matrix-test-on-comment.yml
new file mode 100644
index 0000000000..972cabdb89
--- /dev/null
+++ b/.github/workflows/dispatch-matrix-test-on-comment.yml
@@ -0,0 +1,52 @@
+name: 🤖 Run Matrix Check (On Comment)
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ dispatch-matrix-check:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Check permission
+ id: check-write-permission
+ uses: ./.github/actions/check-permissions
+ with:
+ minimum-permission: "write"
+
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards-release-engineering"
+
+ - name: Invoke matrix testing job
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/test-matrix') && steps.check-write-permission.outputs.has-permission }}
+ env:
+ ISSUE_NR: ${{ github.event.issue.number }}
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ jq -n \
+ --arg issue_nr "$ISSUE_NR" \
+ '{"issue-nr": $issue_nr}' \
+ | \
+ gh workflow run pr-compiler-validation.yml \
+ --json \
+ -R github/codeql-coding-standards-release-engineering
+
+ - uses: actions/github-script@v7
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/test-matrix') && steps.check-write-permission.outputs.has-permission }}
+ with:
+ script: |
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: '🤖 Beep Boop! Matrix Testing for this PR has been initiated. Please check back later for results.
:bulb: If you do not hear back from me please check my status! **I will report even if this PR does not contain files eligible for matrix testing.**'
+ })
diff --git a/.github/workflows/dispatch-release-performance-check.yml b/.github/workflows/dispatch-release-performance-check.yml
new file mode 100644
index 0000000000..5886bb2ea8
--- /dev/null
+++ b/.github/workflows/dispatch-release-performance-check.yml
@@ -0,0 +1,52 @@
+name: 🏁 Run Release Performance Check
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ dispatch-matrix-check:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Check permission
+ id: check-write-permission
+ uses: ./.github/actions/check-permissions
+ with:
+ minimum-permission: "write"
+
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards-release-engineering"
+
+ - name: Invoke performance test
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/test-performance') && steps.check-write-permission.outputs.has-permission }}
+ env:
+ ISSUE_NR: ${{ github.event.issue.number }}
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ jq -n \
+ --arg issue_nr "$ISSUE_NR" \
+ '{"issue-nr": $issue_nr}' \
+ | \
+ gh workflow run pr-performance-testing.yml \
+ --json \
+ -R github/codeql-coding-standards-release-engineering
+
+ - uses: actions/github-script@v7
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/test-performance') && steps.check-write-permission.outputs.has-permission }}
+ with:
+ script: |
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: '🏁 Beep Boop! Performance testing for this PR has been initiated. Please check back later for results. Note that the query package generation step must complete before testing will start so it might be a minute.
:bulb: If you do not hear back from me please check my status! **I will report even if I fail!**'
+ })
diff --git a/.github/workflows/extra-rule-validation.yml b/.github/workflows/extra-rule-validation.yml
index 218d0d171a..02d37f92b2 100644
--- a/.github/workflows/extra-rule-validation.yml
+++ b/.github/workflows/extra-rule-validation.yml
@@ -1,6 +1,8 @@
name: ⚙️ Extra Rule Validation
on:
+ merge_group:
+ types: [checks_requested]
push:
branches:
- main
@@ -19,7 +21,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Check Rules
shell: pwsh
@@ -31,7 +33,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Ensure CPP Shared Rules Have Valid Structure
shell: pwsh
@@ -42,13 +44,13 @@ jobs:
run: scripts/util/Test-SharedImplementationsHaveTestCases.ps1 -Language c -CIMode
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: failure()
with:
name: missing-test-report.csv
path: MissingTestReport*.csv
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: failure()
with:
name: test-report.csv
diff --git a/.github/workflows/finalize-release.yml b/.github/workflows/finalize-release.yml
new file mode 100644
index 0000000000..b3a96f32d3
--- /dev/null
+++ b/.github/workflows/finalize-release.yml
@@ -0,0 +1,133 @@
+name: Finalize Release
+on:
+ pull_request:
+ types:
+ - closed
+ branches:
+ - "rc/**"
+ workflow_dispatch:
+ inputs:
+ ref:
+ description: |
+ The ref of release to finalize (e.g., 'rc/MAJOR.MINOR.PATCH').
+ required: true
+ tool-ref:
+ description: |
+ The ref to the tooling to use for the finalize (e.g., 'rc/MAJOR.MINOR.PATCH').
+ required: false
+
+jobs:
+ finalize-release:
+ if: (github.event_name == 'pull_request' && github.event.pull_request.merged == true) || github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Determine ref
+ env:
+ REF_FROM_INPUT: ${{ inputs.ref }}
+ TOOL_REF_FROM_INPUT: ${{ inputs.tool-ref }}
+ REF_FROM_PR: ${{ github.event.pull_request.merge_commit_sha }}
+ BASE_REF_FROM_PR: ${{ github.event.pull_request.base.ref }}
+ run: |
+ if [[ $GITHUB_EVENT_NAME == "workflow_dispatch" ]]; then
+ echo "REF=$REF_FROM_INPUT" >> "$GITHUB_ENV"
+ echo "TOOL_REF=$TOOL_REF_FROM_INPUT" >> "$GITHUB_ENV"
+ echo "BASE_REF=$REF_FROM_INPUT" >> "$GITHUB_ENV"
+ else
+ echo "REF=$REF_FROM_PR" >> "$GITHUB_ENV"
+ echo "TOOL_REF=$REF_FROM_PR" >> "$GITHUB_ENV"
+ echo "BASE_REF=$BASE_REF_FROM_PR" >> "$GITHUB_ENV"
+ fi
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.REF }}
+ fetch-depth: 0
+ path: release
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.TOOL_REF }}
+ path: tooling
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install dependencies
+ run: pip install -r scripts/release/requirements.txt
+ working-directory: tooling
+
+ - name: Configure git
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+ working-directory: release
+
+ - name: Update release tag
+ run: |
+ version=${BASE_REF#rc/}
+ echo "Creating release tag v$version"
+
+ git tag -f -a v$version -m "Release v$version"
+ git push --force origin v$version
+ working-directory: release
+
+ - name: Finalize release
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ version=${BASE_REF#rc/}
+ echo "Finalizing release v$version"
+
+ gh release edit "v$version" --draft=false --tag=v$version
+ working-directory: release
+
+ - name: Determine if release was a hotfix release
+ run: |
+ version=${BASE_REF#rc/}
+ # We are running the script in the tooling directory with the release directory as the working directory
+ echo "HOTFIX_RELEASE=$(python ../tooling/scripts/release/is-hotfix-release.py $version)" >> "$GITHUB_ENV"
+ working-directory: release
+
+ - name: Determine next release version
+ if: env.HOTFIX_RELEASE == 'false'
+ run: |
+ version=${BASE_REF#rc/}
+ next_version=$(python scripts/release/next-version.py --component minor --pre-release dev -- $version)
+ echo "NEXT_VERSION=$next_version" >> "$GITHUB_ENV"
+ working-directory: tooling
+
+ - name: Generate token
+ if: env.HOTFIX_RELEASE == 'false'
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards"
+
+ - name: Bump main version
+ if: env.HOTFIX_RELEASE == 'false'
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ echo "Bumping main version to $NEXT_VERSION"
+
+ git switch main
+ git pull --ff-only origin main
+
+ git switch -c "release-automation/bump-version-to-$NEXT_VERSION"
+
+ # We are running the script in the tooling directory with the release directory as the working directory
+ ../tooling/scripts/release/bump-version.sh "$NEXT_VERSION"
+
+ git add -u .
+ git commit -m "Bump version to $NEXT_VERSION"
+ git push --set-upstream origin "release-automation/bump-version-to-$NEXT_VERSION"
+
+ gh pr create --repo $GITHUB_REPOSITORY --base main --head "release-automation/bump-version-to-$NEXT_VERSION" --body "Bump the version of main to $NEXT_VERSION" --title "Bump version to $NEXT_VERSION"
+ working-directory: release
diff --git a/.github/workflows/generate-html-docs.yml b/.github/workflows/generate-html-docs.yml
index 1093715ad4..d63f631421 100644
--- a/.github/workflows/generate-html-docs.yml
+++ b/.github/workflows/generate-html-docs.yml
@@ -1,6 +1,8 @@
name: Generate HTML documentation
on:
+ merge_group:
+ types: [checks_requested]
push:
branches:
- main
@@ -18,10 +20,10 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
@@ -33,7 +35,7 @@ jobs:
python scripts/documentation/generate_iso26262_docs.py coding-standards-html-docs
- name: Upload HTML documentation
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: coding-standards-docs-${{ github.sha }}
path: coding-standards-html-docs/
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
new file mode 100644
index 0000000000..75e297d42e
--- /dev/null
+++ b/.github/workflows/prepare-release.yml
@@ -0,0 +1,166 @@
+name: "Prepare CodeQL Coding Standards release"
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: |
+ The version to release (MUST follow semantic versioning so NO 'v' prefix).
+ required: true
+ ref:
+ description: |
+ The git commit, branch, or tag to release from.
+ required: true
+ hotfix:
+ description: |
+ Hotfix release.
+ required: false
+ default: false
+ type: boolean
+
+permissions:
+ contents: write
+ pull-requests: write
+ actions: write
+ checks: write
+
+env:
+ RELEASE_VERSION: ${{ inputs.version }}
+ HOTFIX_RELEASE: ${{ inputs.hotfix }}
+
+jobs:
+ prepare-release:
+ name: "Prepare release"
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.ref }}
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install release script dependencies
+ run: pip install -r scripts/release/requirements.txt
+
+ - name: Validate version
+ run: |
+ if [[ "$HOTFIX_RELEASE" == "true" ]]; then
+ python scripts/release/validate-version.py --hotfix "$RELEASE_VERSION"
+ else
+ python scripts/release/validate-version.py "$RELEASE_VERSION"
+ fi
+
+ - name: Check if release exists
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ release=$( { gh release view "v$RELEASE_VERSION" --json name,isDraft; } || echo "" )
+ if [[ -z "$release" ]]; then
+ echo "Release v$RELEASE_VERSION does not exist. Proceeding"
+ echo "create_draft_release=true" >> "$GITHUB_ENV"
+ else
+ isDraft=$(echo "$release" | jq -r '.isDraft')
+ if [[ "$isDraft" != "true" ]]; then
+ echo "Release 'v$RELEASE_VERSION' already exists and is not a draft. Cannot proceed"
+ exit 1
+ else
+ echo "Release 'v$RELEASE_VERSION' already exists and is a draft. Proceeding"
+ echo "create_draft_release=false" >> "$GITHUB_ENV"
+ fi
+ fi
+
+ - name: Check if release PR exists
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ release_pr=$( { gh pr view "rc/$RELEASE_VERSION" --json title,state,number; } || echo "")
+ if [[ ! -z "$release_pr" ]]; then
+ pr_title=$(echo "$release_pr" | jq -r '.title')
+ pr_state=$(echo "$release_pr" | jq -r '.state')
+ pr_number=$(echo "$release_pr" | jq -r '.number')
+ echo "Found PR '$pr_title' with state '$pr_state'"
+ if [[ "$pr_title" == "Release v$RELEASE_VERSION" ]] && [[ "$pr_state" != "CLOSED" ]]; then
+ echo "Release PR is not closed, deleting it to proceed"
+ gh pr close --delete-branch $pr_number
+ fi
+ fi
+
+ - name: Delete existing release branch
+ run: |
+ if [[ ! -z $(git ls-remote --heads origin rc/$RELEASE_VERSION) ]]; then
+ echo "Deleting existing release branch"
+ git push origin --delete rc/$RELEASE_VERSION
+ fi
+
+ - name: Delete existing feature branch
+ run: |
+ if [[ ! -z $(git ls-remote --heads origin feature/update-user-manual-for-$RELEASE_VERSION) ]]; then
+ echo "Deleting existing feature branch"
+ git push origin --delete feature/update-user-manual-for-$RELEASE_VERSION
+ fi
+
+ - name: Configure git
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+
+ - name: Create release branch
+ run: |
+ git switch -c rc/$RELEASE_VERSION
+ git push --set-upstream origin rc/$RELEASE_VERSION
+
+ - name: Create draft release
+ if: env.create_draft_release == 'true'
+ env:
+ RELEASE_VERSION: ${{ inputs.version }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ # Create lightweight tag to reference release
+ git tag v$RELEASE_VERSION
+ git push -f origin v$RELEASE_VERSION
+
+ gh release create \
+ -R $GITHUB_REPOSITORY \
+ --title "v$RELEASE_VERSION" \
+ --draft \
+ --target rc/$RELEASE_VERSION \
+ v$RELEASE_VERSION
+
+ - name: Create feature branch for PR
+ run: |
+ git switch -c feature/update-user-manual-for-$RELEASE_VERSION
+ git push --set-upstream origin feature/update-user-manual-for-$RELEASE_VERSION
+
+ scripts/release/bump-version.sh "$RELEASE_VERSION"
+
+ git add -u .
+ git commit -m "Update version"
+ git push
+
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards"
+
+ - name: Create release PR
+ env:
+ # Use the token from the `generate-token` step because we can't use the default workflow token
+ # to create a PR and generate PR events to trigger the next workflow because of recursive workflow
+ # trigger protection.
+ GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ gh pr create \
+ -R $GITHUB_REPOSITORY \
+ --title "Release v$RELEASE_VERSION" \
+ --body "This PR releases codeql-coding-standards version $RELEASE_VERSION." \
+ --base rc/$RELEASE_VERSION \
+ --head feature/update-user-manual-for-$RELEASE_VERSION \
+ --draft
diff --git a/.github/workflows/standard_library_upgrade_tests.yml b/.github/workflows/standard_library_upgrade_tests.yml
index a03b43c36f..a401150b07 100644
--- a/.github/workflows/standard_library_upgrade_tests.yml
+++ b/.github/workflows/standard_library_upgrade_tests.yml
@@ -19,7 +19,7 @@ jobs:
matrix: ${{ steps.export-unit-test-matrix.outputs.matrix }}
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Export unit test matrix
id: export-unit-test-matrix
@@ -41,16 +41,16 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup Python 3
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Cache CodeQL
id: cache-codeql
- uses: actions/cache@v2.1.3
+ uses: actions/cache@v4
with:
# A list of files, directories, and wildcard patterns to cache and restore
path: ${{github.workspace}}/codeql_home
@@ -116,7 +116,7 @@ jobs:
stdlib_path = os.path.join(codeql_home, 'codeql-stdlib')
cpp_test_root = Path(stdlib_path, 'cpp/ql/test')
print(f"Executing tests found (recursively) in the directory '{cpp_test_root}'")
- cp = subprocess.run([codeql_bin, "test", "run", "--format=json", f'--search-path={stdlib_path}', cpp_test_root], stdout=test_report_file, stderr=subprocess.PIPE)
+ cp = subprocess.run([codeql_bin, "test", "run", "--format=json", cpp_test_root], stdout=test_report_file, stderr=subprocess.PIPE)
if cp.returncode != 0:
print_error_and_fail(f"Failed to run tests with return code {cp.returncode} and error {cp.stderr}")
@@ -143,7 +143,7 @@ jobs:
}, test_summary_file)
- name: Upload test results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: test-results-${{runner.os}}-${{matrix.codeql_cli}}-${{matrix.codeql_standard_library_ident}}
path: |
@@ -157,12 +157,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
- name: Collect test results
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
- name: Validate test results
shell: python
diff --git a/.github/workflows/tooling-unit-tests.yml b/.github/workflows/tooling-unit-tests.yml
index 5531c11d72..3f4fde932d 100644
--- a/.github/workflows/tooling-unit-tests.yml
+++ b/.github/workflows/tooling-unit-tests.yml
@@ -1,6 +1,8 @@
name: 🧰 Tooling unit tests
on:
+ merge_group:
+ types: [checks_requested]
push:
branches:
- main
@@ -15,12 +17,12 @@ on:
jobs:
prepare-supported-codeql-env-matrix:
name: Prepare supported CodeQL environment matrix
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
outputs:
matrix: ${{ steps.export-supported-codeql-env-matrix.outputs.matrix }}
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Export supported CodeQL environment matrix
id: export-supported-codeql-env-matrix
@@ -32,16 +34,16 @@ jobs:
analysis-report-tests:
name: Run analysis report tests
needs: prepare-supported-codeql-env-matrix
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare-supported-codeql-env-matrix.outputs.matrix) }}
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
@@ -50,7 +52,7 @@ jobs:
- name: Cache CodeQL
id: cache-codeql
- uses: actions/cache@v2.1.3
+ uses: actions/cache@v4
with:
path: ${{ github.workspace }}/codeql_home
key: codeql-home-${{ matrix.os }}-${{ matrix.codeql_cli }}-${{ matrix.codeql_standard_library }}
@@ -64,6 +66,11 @@ jobs:
codeql-home: ${{ github.workspace }}/codeql_home
add-to-path: false
+ - name: Install CodeQL packs
+ uses: ./.github/actions/install-codeql-packs
+ with:
+ cli_path: ${{ github.workspace }}/codeql_home/codeql
+
- name: Run PyTest
env:
CODEQL_HOME: ${{ github.workspace }}/codeql_home
@@ -73,13 +80,13 @@ jobs:
recategorization-tests:
name: Run Guideline Recategorization tests
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
@@ -89,3 +96,22 @@ jobs:
- name: Run PyTest
run: |
pytest scripts/guideline_recategorization/recategorize_test.py
+
+ release-tests:
+ name: Run release tests
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install Python dependencies
+ run: pip install -r scripts/release/requirements.txt
+
+ - name: Run PyTest
+ run: |
+ pytest scripts/release/update_release_assets_test.py
\ No newline at end of file
diff --git a/.github/workflows/update-check-run.yml b/.github/workflows/update-check-run.yml
new file mode 100644
index 0000000000..225c81fa24
--- /dev/null
+++ b/.github/workflows/update-check-run.yml
@@ -0,0 +1,70 @@
+name: Update check run
+
+on:
+ workflow_dispatch:
+ inputs:
+ id:
+ description: |
+ The unique identifier of the check run.
+ type: number
+ required: true
+ status:
+ description: |
+ The current status.
+
+ Can be one of: queued, in_progress, completed
+ type: string
+ required: true
+ conclusion:
+ description: |
+ The final conclusion of the check when completed.
+
+ Can be one of: action_required, cancelled, failure, neutral, success, skipped, stale, timed_out
+ type: string
+ details_url:
+ description: |
+ The URL of the integrator's site that has the full details of the check.
+ type: string
+ external_id:
+ description: |
+ A reference for the run on the integrator's system.
+ type: string
+ output:
+ description: |
+ The output object for the check run.
+
+ See https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#update-a-check-run for more information.
+ type: string
+ default: '{}'
+
+permissions:
+ checks: write
+
+jobs:
+ update-check-run:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Update check run
+ env:
+ CHECK_RUN_ID: ${{ inputs.id }}
+ CHECK_RUN_STATUS: ${{ inputs.status }}
+ CHECK_RUN_CONCLUSION: ${{ inputs.conclusion }}
+ CHECK_RUN_DETAILS_URL: ${{ inputs.details_url }}
+ CHECK_RUN_EXTERNAL_ID: ${{ inputs.external_id }}
+ CHECK_RUN_OUTPUT: ${{ inputs.output }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ jq -n \
+ --arg status "$CHECK_RUN_STATUS" \
+ --arg conclusion "$CHECK_RUN_CONCLUSION" \
+ --arg details_url "$CHECK_RUN_DETAILS_URL" \
+ --arg external_id "$CHECK_RUN_EXTERNAL_ID" \
+ --argjson output "$CHECK_RUN_OUTPUT" \
+ '{status: $status, conclusion: $conclusion, details_url: $details_url, external_id: $external_id, output: $output} | to_entries | map(select(.value != "" and .value != {})) | from_entries' \
+ | \
+ gh api \
+ --method PATCH \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --input - \
+ /repos/$GITHUB_REPOSITORY/check-runs/$CHECK_RUN_ID
diff --git a/.github/workflows/update-release-status.yml b/.github/workflows/update-release-status.yml
new file mode 100644
index 0000000000..874bc4d0b2
--- /dev/null
+++ b/.github/workflows/update-release-status.yml
@@ -0,0 +1,138 @@
+name: "Update Release Status"
+on:
+ workflow_dispatch:
+ inputs:
+ head-sha:
+ description: |
+ The head SHA to use.
+ type: string
+ required: true
+
+permissions:
+ actions: write
+ checks: write
+ contents: write
+
+env:
+ HEAD_SHA: ${{ inputs.head-sha }}
+
+jobs:
+ validate-check-runs:
+ runs-on: ubuntu-22.04
+ outputs:
+ status: ${{ steps.set-output.outputs.status }}
+ conclusion: ${{ steps.set-output.outputs.conclusion }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.head-sha }}
+
+ - name: Get release status check run
+ id: get-check-run
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ check_run_info=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --jq '.check_runs[] | select(.name == "release-status") | {id: .id, status: .status, conclusion: .conclusion}' \
+ /repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/check-runs)
+
+ if [[ -z "$check_run_info" ]]; then
+ echo "No release status check run found"
+ exit 1
+ fi
+
+ check_run_id=$(echo "$check_run_info" | jq -r '.id')
+ check_run_status=$(echo "$check_run_info" | jq -r '.status')
+ check_run_conclusion=$(echo "$check_run_info" | jq -r '.conclusion')
+
+ echo "CHECK_RUN_ID=$check_run_id" >> "$GITHUB_ENV"
+ echo "CHECK_RUN_STATUS=$check_run_status" >> "$GITHUB_ENV"
+ echo "CHECK_RUN_CONCLUSION=$check_run_conclusion" >> "$GITHUB_ENV"
+
+ - name: Reset release status
+ if: env.CHECK_RUN_STATUS == 'completed'
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ check_run_id=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --field name="release-status" \
+ --field head_sha="$HEAD_SHA" \
+ --jq ".id" \
+ /repos/$GITHUB_REPOSITORY/check-runs)
+
+ echo "Created release status check run with id $check_run_id"
+ # Reset the status to in progress.
+ echo "CHECK_RUN_STATUS=in_progress" >> "$GITHUB_ENV"
+ echo "CHECK_RUN_ID=$check_run_id" >> "$GITHUB_ENV"
+
+ - name: Check all runs completed
+ if: env.CHECK_RUN_STATUS != 'completed'
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ check_runs=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --jq '.check_runs | map(select(.name != "release-status"))' \
+ /repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/check-runs)
+
+ status_stats=$(echo "$check_runs" | jq -r '. | {failed: (map(select(.conclusion == "failure")) | length), pending: (map(select(.status != "completed")) | length) }')
+
+ echo "status_stats=$status_stats"
+
+ failed=$(echo "$status_stats" | jq -r '.failed')
+ pending=$(echo "$status_stats" | jq -r '.pending')
+
+ echo "CHECK_RUNS_FAILED=$failed" >> "$GITHUB_ENV"
+ echo "CHECK_RUNS_PENDING=$pending" >> "$GITHUB_ENV"
+
+ - name: Conclude release status
+ if: env.CHECK_RUNS_PENDING == '0' && env.CHECK_RUN_STATUS != 'completed'
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ if [[ "$CHECK_RUNS_FAILED" == "0" ]]; then
+ echo "All check runs succeeded"
+ conclusion="success"
+ else
+ echo "Some check runs failed"
+ conclusion="failure"
+ fi
+
+ jq -n \
+ --arg status "completed" \
+ --arg conclusion "$conclusion" \
+ '{status: $status, conclusion: $conclusion}' \
+ | \
+ gh api \
+ --method PATCH \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --input - \
+ /repos/$GITHUB_REPOSITORY/check-runs/$CHECK_RUN_ID
+
+ echo "RELEASE_STATUS_CONCLUSION=$conclusion" >> "$GITHUB_ENV"
+
+ - name: Set output
+ id: set-output
+ run: |
+ echo "conclusion=$RELEASE_STATUS_CONCLUSION" >> "$GITHUB_OUTPUT"
+ if [[ "$CHECK_RUNS_PENDING" == "0" ]]; then
+ echo "status=completed" >> "$GITHUB_OUTPUT"
+ else
+ echo "status=in_progress" >> "$GITHUB_OUTPUT"
+ fi
+
+ update-release:
+ needs: validate-check-runs
+ if: needs.validate-check-runs.outputs.status == 'completed' && needs.validate-check-runs.outputs.conclusion == 'success'
+ uses: ./.github/workflows/update-release.yml
+ with:
+ head-sha: ${{ inputs.head-sha }}
+ secrets:
+ AUTOMATION_PRIVATE_KEY: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
diff --git a/.github/workflows/update-release.yml b/.github/workflows/update-release.yml
new file mode 100644
index 0000000000..e3b8045514
--- /dev/null
+++ b/.github/workflows/update-release.yml
@@ -0,0 +1,72 @@
+name: Update Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ head-sha:
+ description: |
+ The head SHA of the release PR to use for finalizing the release.
+ required: true
+ workflow_call:
+ inputs:
+ head-sha:
+ type: string
+ description: |
+ The head SHA of the release PR to use for finalizing the release.
+ required: true
+ secrets:
+ AUTOMATION_PRIVATE_KEY:
+ description: |
+ The private key to use to generate a token for accessing the release engineering repository.
+ required: true
+env:
+ HEAD_SHA: ${{ inputs.head-sha }}
+
+jobs:
+ update-release:
+ name: "Update release"
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # We need the full history to compute the changelog
+ ref: ${{ inputs.head-sha }}
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install dependencies
+ run: pip install -r scripts/release/requirements.txt
+
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards-release-engineering"
+
+ - name: Update release assets
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_ENGINEERING_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ python scripts/release/update_release_assets.py \
+ --head-sha $HEAD_SHA \
+ --layout scripts/release/release-layout.yml \
+ --repo "$GITHUB_REPOSITORY" \
+ --github-token "$GITHUB_REPOSITORY:$GITHUB_TOKEN" "github/codeql-coding-standards-release-engineering:$RELEASE_ENGINEERING_TOKEN" \
+ --skip-checkrun "release-status" "Update Release"
+
+ - name: Update release notes
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ python scripts/release/update-release-notes.py \
+ --head-sha $HEAD_SHA \
+ --repo "$GITHUB_REPOSITORY" \
+ --github-token "$GITHUB_TOKEN"
diff --git a/.github/workflows/upgrade_codeql_dependencies.yml b/.github/workflows/upgrade_codeql_dependencies.yml
index b06ec6f49c..1ffc874bb4 100644
--- a/.github/workflows/upgrade_codeql_dependencies.yml
+++ b/.github/workflows/upgrade_codeql_dependencies.yml
@@ -7,51 +7,65 @@ on:
description: |
The version of the CodeQL CLI to be set as the default.
required: true
- codeql_standard_library_commit:
- description: |
- The tag or commit to use from the CodeQL Standard Library
- required: true
env:
XARGS_MAX_PROCS: 4
jobs:
- say_hello:
+ upgrade_codeql_dependencies:
env:
CODEQL_CLI_VERSION: ${{ github.event.inputs.codeql_cli_version }}
- CODEQL_LIB_COMMIT: ${{ github.event.inputs.codeql_standard_library_commit }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
-
- - name: Update the supported environment
- run: |
- jq \
- --arg cli_version "$CODEQL_CLI_VERSION" \
- --arg standard_library_commit "$CODEQL_LIB_COMMIT" \
- --raw-output \
- '.supported_environment | .[0] | .codeql_cli = $cli_version | .codeql_standard_library = $standard_library_commit' \
- supported_codeql_configs.json
+ uses: actions/checkout@v4
- name: Fetch CodeQL
env:
GITHUB_TOKEN: ${{ github.token }}
+ RUNNER_TEMP: ${{ runner.temp }}
run: |
+ cd $RUNNER_TEMP
gh release download "v${CODEQL_CLI_VERSION}" --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip
unzip -q codeql-linux64.zip
+ echo "$RUNNER_TEMP/codeql/" >> $GITHUB_PATH
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install upgrade-codeql-dependencies.py dependencies
+ run: pip install -r scripts/upgrade-codeql-dependencies/requirements.txt
+
+ - name: Update the supported environment
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ CODEQL_CLI_VERSION: ${{ github.event.inputs.codeql_cli_version }}
+ run: |
+ python3 scripts/upgrade-codeql-dependencies/upgrade-codeql-dependencies.py --cli-version "$CODEQL_CLI_VERSION"
- name: Update CodeQL formatting based on new CLI version
+ env:
+ RUNNER_TEMP: ${{ runner.temp }}
run: |
- find cpp -name '*.ql' -or -name '*.qll' | xargs --max-procs "$XARGS_MAX_PROCS" --max-args 1 codeql/codeql query format --in-place
- find c -name '*.ql' -or -name '*.qll' | xargs --max-procs "$XARGS_MAX_PROCS" --max-args 1 codeql/codeql query format --in-place
+ find cpp \( -name '*.ql' -or -name '*.qll' \) -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
+ find c \( -name '*.ql' -or -name '*.qll' \) -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
- name: Create Pull Request
- uses: peter-evans/create-pull-request@v3
+ uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
- title: "Upgrading `github/codeql` dependency to ${{ github.event.inputs.codeql_standard_library_commit }}"
- body: "This PR upgrades the CodeQL CLI version to ${{ github.event.inputs.codeql_cli_version }} and the `github/codeql` version to ${{ github.event.inputs.codeql_standard_library_commit }}."
- commit-message: "Upgrading `github/codeql` dependency to ${{ github.event.inputs.codeql_standard_library_commit }}"
- team-reviewers: github/codeql-coding-standards
+ title: "Upgrade `github/codeql` dependency to ${{ github.event.inputs.codeql_cli_version }}"
+ body: |
+ This PR upgrades the CodeQL CLI version to ${{ github.event.inputs.codeql_cli_version }}.
+
+ ## CodeQL dependency upgrade checklist:
+
+ - [ ] Confirm the code has been correctly reformatted according to the new CodeQL CLI.
+ - [ ] Identify any CodeQL compiler warnings and errors, and update queries as required.
+ - [ ] Validate that the `github/codeql` test cases succeed.
+ - [ ] Address any CodeQL test failures in the `github/codeql-coding-standards` repository.
+ - [ ] Validate performance vs pre-upgrade, using /test-performance
+ commit-message: "Upgrading `github/codeql` dependency to ${{ github.event.inputs.codeql_cli_version }}"
delete-branch: true
- branch: "codeql/upgrade-to-${{ github.event.inputs.codeql_standard_library_commit }}-${{ github.event.inputs.codeql_cli_version }}"
+ branch: "codeql/upgrade-to-${{ github.event.inputs.codeql_cli_version }}"
diff --git a/.github/workflows/validate-coding-standards.yml b/.github/workflows/validate-coding-standards.yml
deleted file mode 100644
index a57634dfc0..0000000000
--- a/.github/workflows/validate-coding-standards.yml
+++ /dev/null
@@ -1,184 +0,0 @@
-name: Validating Coding Standards
-
-on:
- push:
- branches:
- - main
- - "rc/**"
- - next
- pull_request:
- branches:
- - main
- - "rc/**"
- - next
-
-env:
- XARGS_MAX_PROCS: 4
-
-jobs:
- validate-package-files:
- name: Validate Package Files
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Python
- uses: actions/setup-python@v4
- with:
- python-version: "3.9"
-
- - name: Install CodeQL
- run: |
- VERSION="v$( jq -r '.supported_environment | .[0] | .codeql_cli' supported_codeql_configs.json)"
- gh extensions install github/gh-codeql
- gh codeql set-version "$VERSION"
- gh codeql install-stub
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- - name: Install generate_package_files.py dependencies
- run: pip install -r scripts/requirements.txt
-
- - name: Validate Package Descriptions (CPP)
- run: |
- python scripts/validate-rule-package.py rule_packages/cpp/*.json
-
- - name: Validate Package Descriptions (C)
- run: |
- python scripts/validate-rule-package.py rule_packages/c/*.json
-
- - name: Validate Package Descriptions consistency (CPP)
- run: |
- python scripts/verify_rule_package_consistency.py cpp
-
- - name: Validate Package Descriptions consistency (C)
- run: |
- python scripts/verify_rule_package_consistency.py c
-
- - name: Validate Package Files (CPP)
- run: |
- find rule_packages/cpp -name \*.json -exec basename {} .json \; | xargs python scripts/generate_rules/generate_package_files.py cpp
- git diff
- git diff --compact-summary
- git diff --quiet
-
- - name: Validate Package Files (C)
- run: |
- find rule_packages/c -name \*.json -exec basename {} .json \; | xargs python scripts/generate_rules/generate_package_files.py c
- git diff
- git diff --compact-summary
- git diff --quiet
-
- validate-codeql-format:
- name: "Validate CodeQL Format"
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install CodeQL
- run: |
- VERSION="v$( jq -r '.supported_environment | .[0] | .codeql_cli' supported_codeql_configs.json)"
- gh extensions install github/gh-codeql
- gh codeql set-version "$VERSION"
- gh codeql install-stub
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- - name: Validate CodeQL Format (CPP)
- run: |
- find cpp -name \*.ql -or -name \*.qll -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
-
- git diff
- git diff --compact-summary
- git diff --quiet
-
- - name: Validate CodeQL Format (C)
- run: |
- find c -name \*.ql -or -name \*.qll -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
-
- git diff
- git diff --compact-summary
- git diff --quiet
-
- validate-query-help-files:
- name: Validate Query Help Files
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Validate CPP Query Help Files
- run: |
- exit_code=0
- for help_file in `find cpp -name '*.md'`
- do
- if grep -F -q 'REPLACE THIS' "$help_file" > /dev/null
- then
- echo "Help file $help_file contains placeholders that are not replaced or removed!"
- exit_code=1
- fi
- done
-
- exit $exit_code
-
- - name: Validate C Query Help Files
- run: |
- exit_code=0
- for help_file in `find c -name '*.md'`
- do
- if grep -F -q 'REPLACE THIS' "$help_file" > /dev/null
- then
- echo "Help file $help_file contains placeholders that are not replaced or removed!"
- exit_code=1
- fi
- done
-
- exit $exit_code
-
- validate-cpp-test-files:
- name: Validate C++ Test Files
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install clang-format
- run: |
- sudo apt-get install --yes --quiet --no-install-recommends clang-format
- echo "::debug::$(clang-format -version)"
-
- - name: Validate C++ Test Files
- run: |
- if ! test -f .clang-format; then
- echo "Cannot find .clang-format in '$PWD'. Exiting..."
- fi
-
- find cpp/*/test -name \*.cpp -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" clang-format --style=file -i --verbose
- git diff
- git diff --compact-summary
- git diff --quiet
-
- validate-c-test-files:
- name: Validate C Test Files
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install clang-format
- run: |
- sudo apt-get install --yes --quiet --no-install-recommends clang-format
- echo "::debug::$(clang-format -version)"
-
- - name: Validate C++ Test Files
- run: |
- if ! test -f .clang-format; then
- echo "Cannot find .clang-format in '$PWD'. Exiting..."
- fi
-
- find c/*/test -name \*.c -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" clang-format --style=file -i --verbose
- git diff
- git diff --compact-summary
- git diff --quiet
diff --git a/.github/workflows/validate-package-files.yml b/.github/workflows/validate-package-files.yml
new file mode 100644
index 0000000000..a716921053
--- /dev/null
+++ b/.github/workflows/validate-package-files.yml
@@ -0,0 +1,65 @@
+name: Validate Package Files
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ branches:
+ - main
+ - next
+ - "rc/**"
+
+jobs:
+ validate-package-files:
+ strategy:
+ matrix:
+ language: [cpp, c]
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.ref }}
+
+ - name: Install Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Install CodeQL
+ run: |
+ VERSION="v$( jq -r '.supported_environment | .[0] | .codeql_cli' supported_codeql_configs.json)"
+ gh extensions install github/gh-codeql
+ gh codeql set-version "$VERSION"
+ gh codeql install-stub
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+ - name: Install generate_package_files.py dependencies
+ run: pip install -r scripts/requirements.txt
+
+ - name: Validate Package Descriptions
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ python scripts/validate-rule-package.py rule_packages/$LANGUAGE/*.json
+
+ - name: Validate Package Descriptions consistency
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ python scripts/verify_rule_package_consistency.py $LANGUAGE
+
+ - name: Validate Current versus Expected Package Files
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ find rule_packages/$LANGUAGE -name \*.json -exec basename {} .json \; | xargs python scripts/generate_rules/generate_package_files.py $LANGUAGE
+ git diff
+ git diff --compact-summary
+ git diff --quiet
+
+ - name: Validate Amendments
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ python scripts/validate-amendments-csv.py $LANGUAGE
\ No newline at end of file
diff --git a/.github/workflows/validate-query-formatting.yml b/.github/workflows/validate-query-formatting.yml
new file mode 100644
index 0000000000..88b4c0d438
--- /dev/null
+++ b/.github/workflows/validate-query-formatting.yml
@@ -0,0 +1,44 @@
+name: "Validate Query Formatting"
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ branches:
+ - main
+ - next
+ - "rc/**"
+
+env:
+ XARGS_MAX_PROCS: 4
+
+jobs:
+ validate-query-formatting:
+ strategy:
+ matrix:
+ language: [cpp, c]
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.ref }}
+
+ - name: Install CodeQL
+ run: |
+ VERSION="v$( jq -r '.supported_environment | .[0] | .codeql_cli' supported_codeql_configs.json)"
+ gh extensions install github/gh-codeql
+ gh codeql set-version "$VERSION"
+ gh codeql install-stub
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+ - name: Validate query format
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ codeql version
+ find $LANGUAGE \( -name \*.ql -or -name \*.qll \) -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
+
+ git diff
+ git diff --compact-summary
+ git diff --quiet
\ No newline at end of file
diff --git a/.github/workflows/validate-query-help.yml b/.github/workflows/validate-query-help.yml
new file mode 100644
index 0000000000..90f0d16dbc
--- /dev/null
+++ b/.github/workflows/validate-query-help.yml
@@ -0,0 +1,37 @@
+name: Validate Query Help Files
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ branches:
+ - main
+ - next
+ - "rc/**"
+
+jobs:
+ validate-query-help-files:
+ strategy:
+ matrix:
+ language: [cpp, c]
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.ref }}
+
+ - name: Validate Query Help Files
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ run: |
+ exit_code=0
+ for help_file in `find $LANGUAGE -name '*.md'`
+ do
+ if grep -F -q 'REPLACE THIS' "$help_file" > /dev/null
+ then
+ echo "Help file $help_file contains placeholders that are not replaced or removed!"
+ exit_code=1
+ fi
+ done
+
+ exit $exit_code
\ No newline at end of file
diff --git a/.github/workflows/validate-query-test-case-formatting.yml b/.github/workflows/validate-query-test-case-formatting.yml
new file mode 100644
index 0000000000..879c1c9058
--- /dev/null
+++ b/.github/workflows/validate-query-test-case-formatting.yml
@@ -0,0 +1,45 @@
+name: Validate Query Test Case Formatting
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ branches:
+ - main
+ - next
+ - "rc/**"
+
+env:
+ XARGS_MAX_PROCS: 4
+
+jobs:
+ validate-test-case-files:
+ runs-on: ubuntu-22.04
+ strategy:
+ matrix:
+ language: [cpp, c]
+ fail-fast: false
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.ref }}
+
+ - name: Install clang-format
+ run: |
+ sudo apt-get install --yes --quiet --no-install-recommends clang-format
+
+ - name: Validating Current versus Expected Test Case Formatting
+ env:
+ LANGUAGE: ${{ matrix.language }}
+ # IMPORTANT: This step current relies on the fact that a file extension is the same as the language name for simplicity.
+ run: |
+ if ! test -f .clang-format; then
+ echo "Cannot find .clang-format in '$PWD'. Exiting..."
+ fi
+
+ find $LANGUAGE/*/test -name \*.$LANGUAGE -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" clang-format --style=file -i --verbose
+ git diff
+ git diff --compact-summary
+ git diff --quiet
+
+
\ No newline at end of file
diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml
new file mode 100644
index 0000000000..cd7d27f6fa
--- /dev/null
+++ b/.github/workflows/validate-release.yml
@@ -0,0 +1,171 @@
+name: Validate release
+
+on:
+ pull_request:
+ branches:
+ - "rc/**"
+
+permissions:
+ contents: read
+ actions: write
+ checks: write
+
+env:
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+
+jobs:
+ pre-validate-performance:
+ outputs:
+ check-run-id: ${{ steps.create-check-run.outputs.check-run-id }}
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Create check run
+ id: create-check-run
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ check_run_id=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --field name="performance-test" \
+ --field head_sha="$HEAD_SHA" \
+ --jq ".id" \
+ /repos/$GITHUB_REPOSITORY/check-runs)
+
+ echo "check-run-id=$check_run_id" >> "$GITHUB_OUTPUT"
+
+ validate-performance:
+ needs: pre-validate-performance
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards-release-engineering"
+ - name: Invoke performance test
+ env:
+ CHECK_RUN_ID: ${{ needs.pre-validate-performance.outputs.check-run-id }}
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ jq -n \
+ --arg ref "$HEAD_SHA" \
+ --arg check_run_id "$CHECK_RUN_ID" \
+ '{ref: $ref, "check-run-id": $check_run_id}' \
+ | \
+ gh workflow run release-performance-testing.yml \
+ --json \
+ -R github/codeql-coding-standards-release-engineering
+
+ on-failure-validate-performance-dispatch:
+ needs: [pre-validate-performance, validate-performance]
+ if: failure()
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Fail check run status
+ env:
+ CHECK_RUN_ID: ${{ needs.pre-validate-performance.outputs.check-run-id }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ jq -n \
+ --arg status "completed" \
+ --arg conclusion "failure" \
+ '{status: $status, conclusion: $conclusion}' \
+ | \
+ gh api \
+ --method PATCH \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --input - \
+ /repos/$GITHUB_REPOSITORY/check-runs/$CHECK_RUN_ID
+
+ pre-validate-compiler-compatibility:
+ outputs:
+ check-run-id: ${{ steps.create-check-run.outputs.check-run-id }}
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Create check run
+ id: create-check-run
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ check_run_id=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --field name="compiler-compatibility-test" \
+ --field head_sha="$HEAD_SHA" \
+ --jq ".id" \
+ /repos/$GITHUB_REPOSITORY/check-runs)
+
+ echo "check-run-id=$check_run_id" >> "$GITHUB_OUTPUT"
+
+ validate-compiler-compatibility:
+ needs: pre-validate-compiler-compatibility
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.AUTOMATION_APP_ID }}
+ private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: "codeql-coding-standards-release-engineering"
+ - name: Invoke compiler compatibility test
+ env:
+ CHECK_RUN_ID: ${{ needs.pre-validate-compiler-compatibility.outputs.check-run-id }}
+ GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ jq -n \
+ --arg ref "$HEAD_SHA" \
+ --arg check_run_id "$CHECK_RUN_ID" \
+ '{ref: $ref, "check-run-id": $check_run_id}' \
+ | \
+ gh workflow run release-compiler-validation.yml \
+ --json \
+ -R github/codeql-coding-standards-release-engineering
+
+ on-failure-validate-compiler-compatibility-dispatch:
+ needs:
+ [pre-validate-compiler-compatibility, validate-compiler-compatibility]
+ if: failure()
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Fail check run status
+ env:
+ CHECK_RUN_ID: ${{ needs.pre-validate-compiler-compatibility.outputs.check-run-id }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ jq -n \
+ --arg status "completed" \
+ --arg conclusion "failure" \
+ '{status: $status, conclusion: $conclusion}' \
+ | \
+ gh api \
+ --method PATCH \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --input - \
+ /repos/$GITHUB_REPOSITORY/check-runs/$CHECK_RUN_ID
+
+ create-release-status-check-run:
+ name: "Initialize release status monitoring"
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Create release status check run
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ CHECK_RUN_ID=$(gh api \
+ --header "Accept: application/vnd.github+json" \
+ --header "X-GitHub-Api-Version: 2022-11-28" \
+ --field name="release-status" \
+ --field head_sha="$HEAD_SHA" \
+ --field status="in_progress" \
+ --jq ".id" \
+ /repos/$GITHUB_REPOSITORY/check-runs)
+
+ echo "Created release status check run with id $CHECK_RUN_ID for $GITHUB_SHA"
diff --git a/.github/workflows/verify-standard-library-dependencies.yml b/.github/workflows/verify-standard-library-dependencies.yml
new file mode 100644
index 0000000000..06ab4d23e2
--- /dev/null
+++ b/.github/workflows/verify-standard-library-dependencies.yml
@@ -0,0 +1,81 @@
+name: Verify Standard Library Dependencies
+
+# Run this workflow every time the "supported_codeql_configs.json" file or a "qlpack.yml" file is changed
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ branches:
+ - main
+ - "rc/**"
+ - next
+ paths:
+ - "supported_codeql_configs.json"
+ - "**/qlpack.yml"
+ workflow_dispatch:
+
+jobs:
+ prepare-matrix:
+ name: Prepare CodeQL configuration matrix
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.export-matrix.outputs.matrix }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Export unit test matrix
+ id: export-matrix
+ run: |
+ echo "::set-output name=matrix::$(
+ jq --compact-output \
+ '.supported_environment | map([.+{os: "ubuntu-20.04-xl", codeql_standard_library_ident : .codeql_standard_library | sub("\/"; "_")}]) | flatten | {include: .}' \
+ supported_codeql_configs.json
+ )"
+
+ verify-dependencies:
+ name: Verify dependencies
+ needs: prepare-matrix
+
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix: ${{fromJSON(needs.prepare-matrix.outputs.matrix)}}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Python 3
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+
+ - name: Cache CodeQL
+ id: cache-codeql
+ uses: actions/cache@v4
+ with:
+ # A list of files, directories, and wildcard patterns to cache and restore
+ path: ${{github.workspace}}/codeql_home
+ # An explicit key for restoring and saving the cache
+ key: codeql-home-${{matrix.os}}-${{matrix.codeql_cli}}-${{matrix.codeql_standard_library}}
+
+ - name: Install CodeQL
+ if: steps.cache-codeql.outputs.cache-hit != 'true'
+ uses: ./.github/actions/install-codeql
+ with:
+ codeql-cli-version: ${{matrix.codeql_cli}}
+ codeql-stdlib-version: ${{matrix.codeql_standard_library}}
+ codeql-home: ${{ github.workspace }}/codeql_home
+
+ - name: Verify dependencies
+ shell: bash
+ env:
+ CLI_PATH: ${{ github.workspace }}/codeql_home/codeql
+ STDLIB_PATH: ${{ github.workspace }}/codeql_home/codeql-stdlib
+ run: |
+ PATH=$PATH:$CLI_PATH
+ ls $STDLIB_PATH
+ pip install -r scripts/requirements.txt
+ python3 scripts/verify-standard-library-version.py --codeql-repo $STDLIB_PATH --mode verify
+
diff --git a/.gitignore b/.gitignore
index 5466e33c8f..360134b51c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@
# C/C++ build artifacts
*.o
/databases/
+
+# CodeQL build artifacts
+**/.codeql/**
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index af8560fc44..0000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "codeql"]
- path = codeql_modules/codeql
- url = https://github.com/github/codeql.git
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 3735a02e52..74f065ac3b 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -140,6 +140,28 @@
},
"problemMatcher": []
},
+ {
+ "label": "🧪 Standards Automation: Build Case Test DB from test file",
+ "type": "shell",
+ "windows": {
+ "command": ".${pathSeparator}scripts${pathSeparator}.venv${pathSeparator}Scripts${pathSeparator}python.exe scripts${pathSeparator}build_test_database.py ${file}"
+ },
+ "linux": {
+ "command": ".${pathSeparator}scripts${pathSeparator}.venv${pathSeparator}bin${pathSeparator}python3 scripts${pathSeparator}build_test_database.py ${file}"
+ },
+ "osx": {
+ "command": ".${pathSeparator}scripts${pathSeparator}.venv${pathSeparator}bin${pathSeparator}python3 scripts${pathSeparator}build_test_database.py ${file}"
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new",
+ "focus": true
+ },
+ "runOptions": {
+ "reevaluateOnRerun": false
+ },
+ "problemMatcher": []
+ },
{
"label": "📝 Standards Automation: Format CodeQL",
"type": "shell",
@@ -193,13 +215,18 @@
"Classes",
"Comments",
"Contracts1",
+ "Contracts2",
+ "Contracts3",
+ "Contracts4",
+ "Contracts5",
+ "Contracts6",
"Concurrency",
"Concurrency",
"Concurrency1",
"Concurrency2",
"Concurrency3",
- "Concurrency4",
- "Concurrency5",
+ "Concurrency4",
+ "Concurrency5",
"Conditionals",
"Const",
"DeadCode",
@@ -210,6 +237,8 @@
"Declarations4",
"Declarations5",
"Declarations6",
+ "Declarations7",
+ "Declarations8",
"Exceptions1",
"Exceptions2",
"Expressions",
@@ -222,26 +251,37 @@
"Includes",
"Initialization",
"IntegerConversion",
+ "InvalidMemory1",
+ "InvalidMemory2",
"Invariants",
"Iterators",
"Lambdas",
"Language1",
"Language2",
+ "Language3",
"Literals",
"Loops",
"Macros",
+ "Memory1",
+ "Memory2",
+ "Memory3",
"Misc",
"MoveForward",
"Naming",
"Null",
"OperatorInvariants",
"Operators",
+ "OrderOfEvaluation",
+ "OutOfBounds",
"Pointers",
"Pointers1",
"Pointers2",
+ "Pointers3",
+ "Representation",
"Scope",
"SideEffects1",
"SideEffects2",
+ "SideEffects3",
"SmartPointers1",
"SmartPointers2",
"Strings",
diff --git a/README.md b/README.md
index 06668c6676..02c226f84a 100644
--- a/README.md
+++ b/README.md
@@ -6,17 +6,24 @@ This repository contains CodeQL queries and libraries which support various Codi
_Carnegie Mellon and CERT are registered trademarks of Carnegie Mellon University._
-This repository contains CodeQL queries and libraries which support various Coding Standards for the [C++14](https://www.iso.org/standard/64029.html) programming language.
+This repository contains CodeQL queries and libraries which support various Coding Standards for the [C++14](https://www.iso.org/standard/64029.html), [C99](https://www.iso.org/standard/29237.html) and [C11](https://www.iso.org/standard/57853.html) programming languages.
The following coding standards are supported:
-- [AUTOSAR - Guidelines for the use of C++14 language in critical and safety-related systems Release 20-11](https://www.autosar.org/fileadmin/user_upload/standards/adaptive/20-11/AUTOSAR_RS_CPP14Guidelines.pdf)
-- [MISRA C++:2008](https://www.misra.org.uk) (support limited to the rules specified in AUTOSAR 20-11).
+- [AUTOSAR - Guidelines for the use of C++14 language in critical and safety-related systems (Releases R22-11, R20-11, R19-11 and R19-03)](https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf).
- [SEI CERT C++ Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition)](https://resources.sei.cmu.edu/library/asset-view.cfm?assetID=494932)
+- [SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition)](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-c-coding-standard-2016-v01.pdf)
+- [MISRA C 2012, 3rd Edition, 1st revision](https://www.misra.org.uk/product/misra-c2012-third-edition-first-revision/) (incoporating Amendment 1 & Technical Corrigendum 1). In addition, we support the following additional amendments and technical corrigendums:
+ - [MISRA C 2012 Amendment 2](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD2.pdf)
+ - [MISRA C 2012 Technical Corrigendum 2](https://misra.org.uk/app/uploads/2022/04/MISRA-C-2012-TC2.pdf)
+ - [MISRA C 2012 Amendment 3](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD3.pdf)
+ - [MISRA C 2012 Amendment 4](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD4.pdf)
+- [MISRA C 2023](https://misra.org.uk/product/misra-c2023/)
-In addition, the following Coding Standards for the C programming language are under development:
+## :construction: Standards under development :construction:
-- [SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition)](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-c-coding-standard-2016-v01.pdf)
-- [MISRA C 2012](https://www.misra.org.uk/product/misra-c2012-third-edition-first-revision/).
+The following standards are under active development for [C++17](https://www.iso.org/standard/68564.html):
+
+- [MISRA C++ 2023](https://misra.org.uk/product/misra-cpp2023/) - under development - _scheduled for release 2025 Q2/Q3_
## How do I use the CodeQL Coding Standards Queries?
@@ -50,3 +57,5 @@ All header files in [c/common/test/includes/standard-library](./c/common/test/in
---
1This repository incorporates portions of the SEI CERT® Coding Standards available at https://wiki.sei.cmu.edu/confluence/display/seccode/SEI+CERT+Coding+Standards; however, such use does not necessarily constitute or imply an endorsement, recommendation, or favoring by Carnegie Mellon University or its Software Engineering Institute.
+
+
diff --git a/amendments.csv b/amendments.csv
new file mode 100644
index 0000000000..7bcc65327c
--- /dev/null
+++ b/amendments.csv
@@ -0,0 +1,49 @@
+language,standard,amendment,rule_id,supportable,implementation_category,implemented,difficulty
+c,MISRA-C-2012,Amendment3,DIR-4-6,Yes,Expand,Yes,Easy
+c,MISRA-C-2012,Amendment3,DIR-4-9,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Amendment3,DIR-4-11,Yes,Refine,Yes,Import
+c,MISRA-C-2012,Amendment3,RULE-1-4,Yes,Replace,Yes,Easy
+c,MISRA-C-2012,Amendment3,RULE-10-1,Yes,Replace,Yes,Easy
+c,MISRA-C-2012,Amendment3,RULE-10-3,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Amendment3,RULE-10-4,Yes,Refine,Yes,Import
+c,MISRA-C-2012,Amendment3,RULE-10-5,Yes,Expand,Yes,Easy
+c,MISRA-C-2012,Amendment3,RULE-10-7,Yes,Refine,Yes,Import
+c,MISRA-C-2012,Amendment3,RULE-10-8,Yes,Refine,Yes,Import
+c,MISRA-C-2012,Amendment3,RULE-21-11,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment3,RULE-21-12,Yes,Replace,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-11-3,Yes,Expand,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-11-8,Yes,Expand,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-13-2,Yes,Expand,Yes,Very Hard
+c,MISRA-C-2012,Amendment4,RULE-18-6,Yes,Expand,Yes,Medium
+c,MISRA-C-2012,Amendment4,RULE-18-8,Yes,Split,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-2-2,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-2-7,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-3-1,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-8-6,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-8-9,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-9-4,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-10-1,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-18-3,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Amendment4,RULE-1-4,Yes,Replace,Yes,Easy
+c,MISRA-C-2012,Amendment4,RULE-9-1,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,DIR-4-10,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-7-4,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-8-2,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-8-3,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-8-7,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-10-1,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-10-2,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-10-3,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-11-3,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-11-6,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-13-2,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-13-6,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-14-3,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-15-7,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-17-4,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-17-5,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-18-1,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-20-14,No,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-21-19,Yes,Clarification,Yes,Import
+c,MISRA-C-2012,Corrigendum2,RULE-21-20,Yes,Refine,Yes,Easy
+c,MISRA-C-2012,Corrigendum2,RULE-22-9,Yes,Clarification,Yes,Import
diff --git a/apply-configuration/action.yml b/apply-configuration/action.yml
new file mode 100644
index 0000000000..89a702b72a
--- /dev/null
+++ b/apply-configuration/action.yml
@@ -0,0 +1,24 @@
+name: Applies Coding Standard configuration files in the repository
+description: |
+ Installs Python and indexes the CodeQL Coding Standard configuration files in the repository
+
+runs:
+ using: composite
+ steps:
+ - name: Install Python
+ id: cs-install-python
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.9
+ update-environment: false
+ - name: Install dependencies and process files
+ shell: bash
+ run: |
+ install_dir=$(dirname $(dirname "${{ steps.cs-install-python.outputs.python-path }}"))
+ if [[ -z "$LD_LIBRARY_PATH" ]]; then
+ export LD_LIBRARY_PATH="$install_dir/lib"
+ else
+ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$install_dir/lib"
+ fi
+ ${{ steps.cs-install-python.outputs.python-path }} -m pip install -r ${GITHUB_ACTION_PATH}/../scripts/configuration/requirements.txt
+ ${{ steps.cs-install-python.outputs.python-path }} ${GITHUB_ACTION_PATH}/../scripts/configuration/process_coding_standards_config.py
\ No newline at end of file
diff --git a/c/.codeqlmanifest.json b/c/.codeqlmanifest.json
deleted file mode 100644
index 384848fdd1..0000000000
--- a/c/.codeqlmanifest.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{ "provide": [
- "*/src/qlpack.yml",
- "*/test/qlpack.yml" ] }
diff --git a/c/cert/src/codeql-pack.lock.yml b/c/cert/src/codeql-pack.lock.yml
new file mode 100644
index 0000000000..a45ea8f438
--- /dev/null
+++ b/c/cert/src/codeql-pack.lock.yml
@@ -0,0 +1,24 @@
+---
+lockVersion: 1.0.0
+dependencies:
+ codeql/cpp-all:
+ version: 4.0.3
+ codeql/dataflow:
+ version: 2.0.3
+ codeql/mad:
+ version: 1.0.19
+ codeql/rangeanalysis:
+ version: 1.0.19
+ codeql/ssa:
+ version: 1.0.19
+ codeql/tutorial:
+ version: 1.0.19
+ codeql/typeflow:
+ version: 1.0.19
+ codeql/typetracking:
+ version: 2.0.3
+ codeql/util:
+ version: 2.0.6
+ codeql/xml:
+ version: 1.0.19
+compiled: false
diff --git a/c/cert/src/codeql-suites/cert-c-default.qls b/c/cert/src/codeql-suites/cert-c-default.qls
new file mode 100644
index 0000000000..348d2f37ae
--- /dev/null
+++ b/c/cert/src/codeql-suites/cert-c-default.qls
@@ -0,0 +1,10 @@
+- description: CERT C 2016 (Default)
+- qlpack: codeql/cert-c-coding-standards
+- include:
+ kind:
+ - problem
+ - path-problem
+ - external/cert/obligation/rule
+- exclude:
+ tags contain:
+ - external/cert/default-disabled
diff --git a/c/cert/src/codeql-suites/cert-c-l1.qls b/c/cert/src/codeql-suites/cert-c-l1.qls
new file mode 100644
index 0000000000..b2056fbec8
--- /dev/null
+++ b/c/cert/src/codeql-suites/cert-c-l1.qls
@@ -0,0 +1,12 @@
+- description: CERT C 2016 Level 1 Rules (Priority 12 - Priority 27)
+- qlpack: codeql/cert-c-coding-standards
+- include:
+ kind:
+ - problem
+ - path-problem
+ - external/cert/obligation/rule
+ tags contain:
+ - external/cert/level/l1
+- exclude:
+ tags contain:
+ - external/cert/default-disabled
\ No newline at end of file
diff --git a/c/cert/src/codeql-suites/cert-c-l2.qls b/c/cert/src/codeql-suites/cert-c-l2.qls
new file mode 100644
index 0000000000..9c0a4b1ef9
--- /dev/null
+++ b/c/cert/src/codeql-suites/cert-c-l2.qls
@@ -0,0 +1,12 @@
+- description: CERT C 2016 Level 2 Rules (Priority 6 - Priority 9)
+- qlpack: codeql/cert-c-coding-standards
+- include:
+ kind:
+ - problem
+ - path-problem
+ - external/cert/obligation/rule
+ tags contain:
+ - external/cert/level/l2
+- exclude:
+ tags contain:
+ - external/cert/default-disabled
\ No newline at end of file
diff --git a/c/cert/src/codeql-suites/cert-c-l3.qls b/c/cert/src/codeql-suites/cert-c-l3.qls
new file mode 100644
index 0000000000..462a6d816f
--- /dev/null
+++ b/c/cert/src/codeql-suites/cert-c-l3.qls
@@ -0,0 +1,12 @@
+- description: CERT C 2016 Level 3 Rules (Priority 1 - Priority 4)
+- qlpack: codeql/cert-c-coding-standards
+- include:
+ kind:
+ - problem
+ - path-problem
+ - external/cert/obligation/rule
+ tags contain:
+ - external/cert/level/l3
+- exclude:
+ tags contain:
+ - external/cert/default-disabled
\ No newline at end of file
diff --git a/c/cert/src/codeql-suites/cert-c-recommendation.qls b/c/cert/src/codeql-suites/cert-c-recommendation.qls
new file mode 100644
index 0000000000..59ac5e9c2d
--- /dev/null
+++ b/c/cert/src/codeql-suites/cert-c-recommendation.qls
@@ -0,0 +1,10 @@
+- description: CERT C 2016 (Recommendations)
+- qlpack: codeql/cert-c-coding-standards
+- include:
+ kind:
+ - problem
+ - path-problem
+ - external/cert/obligation/recommendation
+- exclude:
+ tags contain:
+ - external/cert/default-disabled
diff --git a/c/cert/src/codeql-suites/cert-default.qls b/c/cert/src/codeql-suites/cert-default.qls
index c8652930e9..c093b31fa7 100644
--- a/c/cert/src/codeql-suites/cert-default.qls
+++ b/c/cert/src/codeql-suites/cert-default.qls
@@ -1,9 +1,2 @@
-- description: CERT C 2016 (Default)
-- qlpack: cert-c-coding-standards
-- include:
- kind:
- - problem
- - path-problem
-- exclude:
- tags contain:
- - external/cert/default-disabled
\ No newline at end of file
+- description: "DEPRECATED - CERT C 2016 - use cert-c-default.qls instead"
+- import: codeql-suites/cert-c-default.qls
diff --git a/c/cert/src/qlpack.yml b/c/cert/src/qlpack.yml
index 746f1c3d84..f79744a4fc 100644
--- a/c/cert/src/qlpack.yml
+++ b/c/cert/src/qlpack.yml
@@ -1,4 +1,9 @@
-name: cert-c-coding-standards
-version: 2.13.0-dev
+name: codeql/cert-c-coding-standards
+version: 2.49.0-dev
+description: CERT C 2016
suites: codeql-suites
-libraryPathDependencies: common-c-coding-standards
\ No newline at end of file
+license: MIT
+default-suite-file: codeql-suites/cert-c-default.qls
+dependencies:
+ codeql/common-c-coding-standards: '*'
+ codeql/cpp-all: 4.0.3
diff --git a/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.md b/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.md
new file mode 100644
index 0000000000..221b008786
--- /dev/null
+++ b/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.md
@@ -0,0 +1,485 @@
+# ARR30-C: Do not form or use out-of-bounds pointers or array subscripts
+
+This query implements the CERT-C rule ARR30-C:
+
+> Do not form or use out-of-bounds pointers or array subscripts
+
+
+## Description
+
+The C Standard identifies the following distinct situations in which undefined behavior (UB) can arise as a result of invalid pointer operations:
+
+
UB | Description | Example Code |
46 | Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that does not point into, or just beyond, the same array object. | Forming Out-of-Bounds Pointer , Null Pointer Arithmetic |
47 | Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that points just beyond the array object and is used as the operand of a unary \* operator that is evaluated. | Dereferencing Past the End Pointer , Using Past the End Index |
49 | An array subscript is out of range, even if an object is apparently accessible with the given subscript, for example, in the lvalue expression a\[1\]\[7\] given the declaration int a\[4\]\[5\] ). | Apparently Accessible Out-of-Range Index |
62 | An attempt is made to access, or generate a pointer to just past, a flexible array member of a structure when the referenced object provides no elements for that array. | Pointer Past Flexible Array Member |
+
+
+## Noncompliant Code Example (Forming Out-of-Bounds Pointer)
+
+In this noncompliant code example, the function `f()` attempts to validate the `index` before using it as an offset to the statically allocated `table` of integers. However, the function fails to reject negative `index` values. When `index` is less than zero, the behavior of the addition expression in the return statement of the function is [undefined behavior 46](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_46). On some implementations, the addition alone can trigger a hardware trap. On other implementations, the addition may produce a result that when dereferenced triggers a hardware trap. Other implementations still may produce a dereferenceable pointer that points to an object distinct from `table`. Using such a pointer to access the object may lead to information exposure or cause the wrong object to be modified.
+
+```cpp
+enum { TABLESIZE = 100 };
+
+static int table[TABLESIZE];
+
+int *f(int index) {
+ if (index < TABLESIZE) {
+ return table + index;
+ }
+ return NULL;
+}
+
+```
+
+## Compliant Solution
+
+One compliant solution is to detect and reject invalid values of `index` if using them in pointer arithmetic would result in an invalid pointer:
+
+```cpp
+enum { TABLESIZE = 100 };
+
+static int table[TABLESIZE];
+
+int *f(int index) {
+ if (index >= 0 && index < TABLESIZE) {
+ return table + index;
+ }
+ return NULL;
+}
+
+```
+
+## Compliant Solution
+
+Another slightly simpler and potentially more efficient compliant solution is to use an unsigned type to avoid having to check for negative values while still rejecting out-of-bounds positive values of `index`:
+
+```cpp
+#include
+
+enum { TABLESIZE = 100 };
+
+static int table[TABLESIZE];
+
+int *f(size_t index) {
+ if (index < TABLESIZE) {
+ return table + index;
+ }
+ return NULL;
+}
+
+```
+
+## Noncompliant Code Example (Dereferencing Past-the-End Pointer)
+
+This noncompliant code example shows the flawed logic in the Windows Distributed Component Object Model (DCOM) Remote Procedure Call (RPC) interface that was exploited by the W32.Blaster.Worm. The error is that the `while` loop in the `GetMachineName()` function (used to extract the host name from a longer string) is not sufficiently bounded. When the character array pointed to by `pwszTemp` does not contain the backslash character among the first `MAX_COMPUTERNAME_LENGTH_FQDN + 1` elements, the final valid iteration of the loop will dereference past the end pointer, resulting in exploitable [undefined behavior 47](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_47). In this case, the actual exploit allowed the attacker to inject executable code into a running program. Economic damage from the Blaster worm has been estimated to be at least $525 million \[[Pethia 2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Pethia03)\].
+
+For a discussion of this programming error in the Common Weakness Enumeration database, see [CWE-119](http://cwe.mitre.org/data/definitions/119.html), "Improper Restriction of Operations within the Bounds of a Memory Buffer," and [CWE-121](http://cwe.mitre.org/data/definitions/121.html), "Stack-based Buffer Overflow" \[[MITRE 2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-MITRE)\].
+
+```cpp
+error_status_t _RemoteActivation(
+ /* ... */, WCHAR *pwszObjectName, ... ) {
+ *phr = GetServerPath(
+ pwszObjectName, &pwszObjectName);
+ /* ... */
+}
+
+HRESULT GetServerPath(
+ WCHAR *pwszPath, WCHAR **pwszServerPath ){
+ WCHAR *pwszFinalPath = pwszPath;
+ WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH_FQDN+1];
+ hr = GetMachineName(pwszPath, wszMachineName);
+ *pwszServerPath = pwszFinalPath;
+}
+
+HRESULT GetMachineName(
+ WCHAR *pwszPath,
+ WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH_FQDN+1])
+{
+ pwszServerName = wszMachineName;
+ LPWSTR pwszTemp = pwszPath + 2;
+ while (*pwszTemp != L'\\')
+ *pwszServerName++ = *pwszTemp++;
+ /* ... */
+}
+
+```
+
+## Compliant Solution
+
+In this compliant solution, the `while` loop in the `GetMachineName()` function is bounded so that the loop terminates when a backslash character is found, the null-termination character (`L'\0'`) is discovered, or the end of the buffer is reached. Or, as coded, the while loop continues as long as each character is neither a backslash nor a null character and is not at the end of the buffer. This code does not result in a buffer overflow even if no backslash character is found in `wszMachineName`.
+
+```cpp
+HRESULT GetMachineName(
+ wchar_t *pwszPath,
+ wchar_t wszMachineName[MAX_COMPUTERNAME_LENGTH_FQDN+1])
+{
+ wchar_t *pwszServerName = wszMachineName;
+ wchar_t *pwszTemp = pwszPath + 2;
+ wchar_t *end_addr
+ = pwszServerName + MAX_COMPUTERNAME_LENGTH_FQDN;
+ while ((*pwszTemp != L'\\') &&
+ (*pwszTemp != L'\0') &&
+ (pwszServerName < end_addr))
+ {
+ *pwszServerName++ = *pwszTemp++;
+ }
+
+ /* ... */
+}
+
+```
+This compliant solution is for illustrative purposes and is not necessarily the solution implemented by Microsoft. This particular solution may not be correct because there is no guarantee that a backslash is found.
+
+## Noncompliant Code Example (Using Past-the-End Index)
+
+Similar to the [dereferencing-past-the-end-pointer](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts#ARR30C.Donotformoruseoutofboundspointersorarraysubscripts-DereferencingPasttheEndPointer) error, the function `insert_in_table()` in this noncompliant code example uses an otherwise valid index to attempt to store a value in an element just past the end of an array.
+
+First, the function incorrectly validates the index `pos` against the size of the buffer. When `pos` is initially equal to `size`, the function attempts to store `value` in a memory location just past the end of the buffer.
+
+Second, when the index is greater than `size`, the function modifies `size` before growing the size of the buffer. If the call to `realloc()` fails to increase the size of the buffer, the next call to the function with a value of `pos` equal to or greater than the original value of `size` will again attempt to store `value` in a memory location just past the end of the buffer or beyond.
+
+Third, the function violates [INT30-C. Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap), which could lead to wrapping when 1 is added to `pos` or when `size` is multiplied by the size of `int`.
+
+For a discussion of this programming error in the Common Weakness Enumeration database, see [CWE-122](http://cwe.mitre.org/data/definitions/122.html), "Heap-based Buffer Overflow," and [CWE-129](http://cwe.mitre.org/data/definitions/129.html), "Improper Validation of Array Index" \[[MITRE 2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-MITRE)\].
+
+```cpp
+#include
+
+static int *table = NULL;
+static size_t size = 0;
+
+int insert_in_table(size_t pos, int value) {
+ if (size < pos) {
+ int *tmp;
+ size = pos + 1;
+ tmp = (int *)realloc(table, sizeof(*table) * size);
+ if (tmp == NULL) {
+ return -1; /* Failure */
+ }
+ table = tmp;
+ }
+
+ table[pos] = value;
+ return 0;
+}
+
+```
+
+## Compliant Solution
+
+This compliant solution correctly validates the index `pos` by using the `<=` relational operator, ensures the multiplication will not overflow, and avoids modifying `size` until it has verified that the call to `realloc()` was successful:
+
+```cpp
+#include
+#include
+
+static int *table = NULL;
+static size_t size = 0;
+
+int insert_in_table(size_t pos, int value) {
+ if (size <= pos) {
+ if ((SIZE_MAX - 1 < pos) ||
+ ((pos + 1) > SIZE_MAX / sizeof(*table))) {
+ return -1;
+ }
+
+ int *tmp = (int *)realloc(table, sizeof(*table) * (pos + 1));
+ if (tmp == NULL) {
+ return -1;
+ }
+ /* Modify size only after realloc() succeeds */
+ size = pos + 1;
+ table = tmp;
+ }
+
+ table[pos] = value;
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example (Apparently Accessible Out-of-Range Index)
+
+This noncompliant code example declares `matrix` to consist of 7 rows and 5 columns in row-major order. The function `init_matrix` iterates over all 35 elements in an attempt to initialize each to the value given by the function argument `x`. However, because multidimensional arrays are declared in C in row-major order, the function iterates over the elements in column-major order, and when the value of `j` reaches the value `COLS` during the first iteration of the outer loop, the function attempts to access element `matrix[0][5]`. Because the type of `matrix` is `int[7][5]`, the `j` subscript is out of range, and the access has [undefined behavior 49](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_49).
+
+```cpp
+#include
+#define COLS 5
+#define ROWS 7
+static int matrix[ROWS][COLS];
+
+void init_matrix(int x) {
+ for (size_t i = 0; i < COLS; i++) {
+ for (size_t j = 0; j < ROWS; j++) {
+ matrix[i][j] = x;
+ }
+ }
+}
+
+```
+
+## Compliant Solution
+
+This compliant solution avoids using out-of-range indices by initializing `matrix` elements in the same row-major order as multidimensional objects are declared in C:
+
+```cpp
+#include
+#define COLS 5
+#define ROWS 7
+static int matrix[ROWS][COLS];
+
+void init_matrix(int x) {
+ for (size_t i = 0; i < ROWS; i++) {
+ for (size_t j = 0; j < COLS; j++) {
+ matrix[i][j] = x;
+ }
+ }
+}
+
+```
+
+## Noncompliant Code Example (Pointer Past Flexible Array Member)
+
+In this noncompliant code example, the function `find()` attempts to iterate over the elements of the flexible array member `buf`, starting with the second element. However, because function `g()` does not allocate any storage for the member, the expression `first++` in `find()` attempts to form a pointer just past the end of `buf` when there are no elements. This attempt is [undefined behavior 62](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_62). (See [MSC21-C. Use robust loop termination conditions](https://wiki.sei.cmu.edu/confluence/display/c/MSC21-C.+Use+robust+loop+termination+conditions) for more information.)
+
+```cpp
+#include
+
+struct S {
+ size_t len;
+ char buf[]; /* Flexible array member */
+};
+
+const char *find(const struct S *s, int c) {
+ const char *first = s->buf;
+ const char *last = s->buf + s->len;
+
+ while (first++ != last) { /* Undefined behavior */
+ if (*first == (unsigned char)c) {
+ return first;
+ }
+ }
+ return NULL;
+}
+
+void g(void) {
+ struct S *s = (struct S *)malloc(sizeof(struct S));
+ if (s == NULL) {
+ /* Handle error */
+ }
+ s->len = 0;
+ find(s, 'a');
+}
+```
+
+## Compliant Solution
+
+This compliant solution avoids incrementing the pointer unless a value past the pointer's current value is known to exist:
+
+```cpp
+#include
+
+struct S {
+ size_t len;
+ char buf[]; /* Flexible array member */
+};
+
+const char *find(const struct S *s, int c) {
+ const char *first = s->buf;
+ const char *last = s->buf + s->len;
+
+ while (first != last) { /* Avoid incrementing here */
+ if (*++first == (unsigned char)c) {
+ return first;
+ }
+ }
+ return NULL;
+}
+
+void g(void) {
+ struct S *s = (struct S *)malloc(sizeof(struct S));
+ if (s == NULL) {
+ /* Handle error */
+ }
+ s->len = 0;
+ find(s, 'a');
+}
+```
+
+## Noncompliant Code Example (Null Pointer Arithmetic)
+
+This noncompliant code example is similar to an [Adobe Flash Player vulnerability](http://www.iss.net/threats/289.html) that was first exploited in 2008. This code allocates a block of memory and initializes it with some data. The data does not belong at the beginning of the block, which is left uninitialized. Instead, it is placed `offset` bytes within the block. The function ensures that the data fits within the allocated block.
+
+```cpp
+#include
+#include
+
+char *init_block(size_t block_size, size_t offset,
+ char *data, size_t data_size) {
+ char *buffer = malloc(block_size);
+ if (data_size > block_size || block_size - data_size < offset) {
+ /* Data won't fit in buffer, handle error */
+ }
+ memcpy(buffer + offset, data, data_size);
+ return buffer;
+}
+```
+This function fails to check if the allocation succeeds, which is a violation of [ERR33-C. Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors). If the allocation fails, then `malloc()` returns a null pointer. The null pointer is added to `offset` and passed as the destination argument to `memcpy()`. Because a null pointer does not point to a valid object, the result of the pointer arithmetic is [undefined behavior 46](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_46).
+
+An attacker who can supply the arguments to this function can exploit it to execute arbitrary code. This can be accomplished by providing an overly large value for `block_size`, which causes `malloc()` to fail and return a null pointer. The `offset` argument will then serve as the destination address to the call to `memcpy()`. The attacker can specify the `data` and `data_size` arguments to provide the address and length of the address, respectively, that the attacker wishes to write into the memory referenced by `offset`. The overall result is that the call to `memcpy()` can be exploited by an attacker to overwrite an arbitrary memory location with an attacker-supplied address, typically resulting in arbitrary code execution.
+
+## Compliant Solution (Null Pointer Arithmetic)
+
+This compliant solution ensures that the call to `malloc()` succeeds:
+
+```cpp
+#include
+#include
+
+char *init_block(size_t block_size, size_t offset,
+ char *data, size_t data_size) {
+ char *buffer = malloc(block_size);
+ if (NULL == buffer) {
+ /* Handle error */
+ }
+ if (data_size > block_size || block_size - data_size < offset) {
+ /* Data won't fit in buffer, handle error */
+ }
+ memcpy(buffer + offset, data, data_size);
+ return buffer;
+}
+
+```
+
+## Risk Assessment
+
+Writing to out-of-range pointers or array subscripts can result in a buffer overflow and the execution of arbitrary code with the permissions of the vulnerable process. Reading from out-of-range pointers or array subscripts can result in unintended information disclosure.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR30-C | High | Likely | High | P9 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | array-index-rangearray-index-range-constantnull-dereferencingpointered-deallocation return-reference-local | Partially checked Can detect all accesses to invalid pointers as well as array index out-of-bounds accesses and prove their absence. This rule is only partially checked as invalid but unused pointers may not be reported. |
Axivion Bauhaus Suite | 7.2.0 | CertC-ARR30 | Can detect out-of-bound access to array / buffer |
CodeSonar | 7.3p0 | LANG.MEM.BO LANG.MEM.BU LANG.MEM.TBA LANG.MEM.TO LANG.MEM.TULANG.STRUCT.PARITH LANG.STRUCT.PBB LANG.STRUCT.PPE BADFUNC.BO.\* | Buffer overrun Buffer underrun Tainted buffer access Type overrun Type underrun Pointer Arithmetic Pointer before beginning of object Pointer past end of object A collection of warning classes that report uses of library functions prone to internal buffer overflows. |
Compass/ROSE | | | Could be configured to catch violations of this rule. The way to catch the noncompliant code example is to first hunt for example code that follows this pattern: for (LPWSTR pwszTemp = pwszPath + 2; \*pwszTemp != L'\\\\'; \*pwszTemp++;) In particular, the iteration variable is a pointer, it gets incremented, and the loop condition does not set an upper bound on the pointer. Once this case is handled, ROSE can handle cases like the real noncompliant code example, which is effectively the same semantics, just different syntax |
Coverity | 2017.07 | OVERRUN NEGATIVE_RETURNS ARRAY_VS_SINGLETON BUFFER_SIZE | Can detect the access of memory past the end of a memory buffer/array Can detect when the loop bound may become negative Can detect the out-of-bound read/write to array allocated statically or dynamically Can detect buffer overflows |
Cppcheck | 1.66 | arrayIndexOutOfBounds, outOfBounds, negativeIndex, arrayIndexThenCheck, arrayIndexOutOfBoundsCond, possibleBufferAccessOutOfBounds | Context sensitive analysis of array index, pointers, etc. Array index out of bounds Buffer overflow when calling various functions memset,strcpy,.. Warns about condition (a\[i\] == 0 && i < unknown_value) and recommends that (i < unknown_value && a\[i\] == 0) is used instead Detects unsafe code when array is accessed before/after it is tested if the array index is out of bounds |
Helix QAC | 2023.1 | C2840 DF2820, DF2821, DF2822, DF2823, DF2840, DF2841, DF2842, DF2843, DF2930, DF2931, DF2932, DF2933, DF2935, DF2936, DF2937, DF2938, DF2950, DF2951, DF2952, DF2953 | |
Klocwork | 2023.1 | ABV.GENERAL ABV.GENERAL.MULTIDIMENSION NPD.FUNC.CALL.MIGHT ABV.ANY_SIZE_ARRAY ABV.STACK ABV.TAINTED ABV.UNICODE.BOUND_MAP ABV.UNICODE.FAILED_MAP ABV.UNICODE.NNTS_MAP ABV.UNICODE.SELF_MAP ABV.UNKNOWN_SIZE NNTS.MIGHT NNTS.MUST NNTS.TAINTED SV.TAINTED.INDEX_ACCESS SV.TAINTED.LOOP_BOUND | |
LDRA tool suite | 9.7.1 | 45 D, 47 S, 476 S, 489 S, 64 X, 66 X, 68 X, 69 X, 70 X, 71 X , 79 X | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-ARR30-a | Avoid accessing arrays out of bounds |
Parasoft Insure++ | | | Runtime analysis |
PC-lint Plus | 1.4 | 413, 415, 416, 613, 661, 662, 676 | Fully supported |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR30-C | Checks for: Array access out of boundsrray access out of bounds, pointer access out of boundsointer access out of bounds, array access with tainted indexrray access with tainted index, use of tainted pointerse of tainted pointer, pointer dereference with tainted offsetointer dereference with tainted offset. Rule partially covered. |
PRQA QA-C | 9.7 | 2820, 2821, 2822, 2823, 2840, 2841, 2842, 2843, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938, 2950, 2951, 2952, 2953 | Partially implemented |
PRQA QA-C++ | 4.4 | 2820, 2821, 2822, 2823, 2840, 2841, 2842, 2843, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938, 2950, 2951, 2952, 2953 | Partially implemented |
PVS-Studio | 7.24 | V512 , V557 , V582 , V594 , V643 , V645 , V694, V1086 | |
RuleChecker | 22.04 | array-index-range-constantreturn-reference-local | Partially checked |
TrustInSoft Analyzer | 1.38 | index_in_address | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+[CVE-2008-1517](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2008-1517) results from a violation of this rule. Before Mac OSX version 10.5.7, the XNU kernel accessed an array at an unverified user-input index, allowing an attacker to execute arbitrary code by passing an index greater than the length of the array and therefore accessing outside memory \[[xorl 2009](http://xorl.wordpress.com/2009/06/09/cve-2008-1517-apple-mac-os-x-xnu-missing-array-index-validation/)\].
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR30-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+ Taxonomy | Taxonomy item | Relationship |
ISO/IEC TR 24772:2013 | Arithmetic Wrap-Around Error \[FIF\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Unchecked Array Indexing \[XYZ\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961 | Forming or using out-of-bounds pointers or array subscripts \[invptr\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
CWE 2.11 | CWE-119 , Improper Restriction of Operations within the Bounds of a Memory Buffer | 2017-05-18: CERT: Rule subset of CWE |
CWE 2.11 | CWE-123 , Write-what-where Condition | 2017-05-18: CERT: Partial overlap |
CWE 2.11 | CWE-125 , Out-of-bounds Read | 2017-05-18: CERT: Partial overlap |
MISRA C:2012 | Rule 18.1 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-119 and ARR30-C**
+
+Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C)
+
+STR31-C = Subset( Union( ARR30-C, ARR38-C))
+
+STR32-C = Subset( ARR38-C)
+
+CWE-119 = Union( ARR30-C, ARR38-C)
+
+Intersection( ARR30-C, ARR38-C) = Ø
+
+**CWE-394 and ARR30-C**
+
+Intersection( ARR30-C, CWE-394) = Ø
+
+CWE-394 deals with potentially-invalid function return values. Which may be used as an (invalid) array index, but validating the return value is a separate operation.
+
+**CWE-125 and ARR30-C**
+
+Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C)
+
+STR31-C = Subset( Union( ARR30-C, ARR38-C))
+
+STR32-C = Subset( ARR38-C)
+
+CWE-125 = Subset( CWE-119) = Union( ARR30-C, ARR38-C)
+
+Intersection( ARR30-C, CWE-125) =
+
+* Reading from an out-of-bounds array index, or off the end of an array
+ARR30-C – CWE-125 =
+* Writing to an out-of-bounds array index, or off the end of an array
+CWE-125 – ARR30-C =
+* Reading beyond a non-array buffer
+* Using a library function to achieve an out-of-bounds read.
+**CWE-123 and ARR30-C**
+
+Independent(ARR30-C, ARR38-C)
+
+STR31-C = Subset( Union( ARR30-C, ARR38-C))
+
+STR32-C = Subset( ARR38-C)
+
+Intersection( CWE-123, ARR30-C) =
+
+* Write of arbitrary value to arbitrary (probably invalid) array index
+ARR30-C – CWE-123 =
+* Read of value from arbitrary (probably invalid) array index
+* Construction of invalid index (pointer arithmetic)
+CWE-123 – ARR30-C =
+* Arbitrary writes that do not involve directly constructing an invalid array index
+**CWE-129 and ARR30-C**
+
+Independent( ARR30-C, ARR32-C, INT31-C, INT32-C)
+
+ARR30-C = Union( CWE-129, list), where list =
+
+* Dereferencing an out-of-bounds array index, where index is a trusted value
+* Forming an out-of-bounds array index, without dereferencing it, whether or not index is a trusted value. (This excludes the array’s TOOFAR index, which is one past the final element; this behavior is well-defined in C11.)
+**CWE-120 and ARR30-C**
+
+See CWE-120 and MEM35-C
+
+**CWE-122 and ARR30-C**
+
+Intersection( ARR30-C, CWE-122) = Ø
+
+CWE-122 specifically addresses buffer overflows on the heap operations, which occur in the context of string-copying. ARR30 specifically addresses improper creation or references of array indices. Which might happen as part of a heap buffer overflow, but is on a lower programming level.
+
+**CWE-20 and ARR30-C**
+
+See CWE-20 and ERR34-C
+
+**CWE-687 and ARR30-C**
+
+Intersection( CWE-687, ARR30-C) = Ø
+
+ARR30-C is about invalid array indices which are created through pointer arithmetic, and dereferenced through an operator (\* or \[\]). Neither involve function calls, thus CWE-687 does not apply.
+
+**CWE-786 and ARR30-C**
+
+ARR30-C = Union( CWE-786, list) where list =
+
+* Access of memory location after end of buffer
+* Construction of invalid arry reference (pointer). This does not include an out-of-bounds array index (an integer).
+**CWE-789 and ARR30-C**
+
+Intersection( CWE-789, ARR30-C) = Ø
+
+CWE-789 is about allocating memory, not array subscripting
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR30-C: Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.ql b/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.ql
new file mode 100644
index 0000000000..fed579bf34
--- /dev/null
+++ b/c/cert/src/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.ql
@@ -0,0 +1,58 @@
+/**
+ * @id c/cert/do-not-form-out-of-bounds-pointers-or-array-subscripts
+ * @name ARR30-C: Do not form or use out-of-bounds pointers or array subscripts
+ * @description Forming or using an out-of-bounds pointer is undefined behavior and can result in
+ * invalid memory accesses.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/arr30-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p9
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.c.OutOfBounds
+
+from
+ OOB::BufferAccess ba, Expr bufferArg, Expr sizeArg, OOB::PointerToObjectSource bufferSource,
+ string message
+where
+ not isExcluded(ba, OutOfBoundsPackage::doNotFormOutOfBoundsPointersOrArraySubscriptsQuery()) and
+ // exclude loops
+ not exists(Loop loop | loop.getStmt().getChildStmt*() = ba.getEnclosingStmt()) and
+ // exclude size arguments that are of type ssize_t
+ not sizeArg.getAChild*().(VariableAccess).getTarget().getType() instanceof Ssize_t and
+ // exclude size arguments that are assigned the result of a function call e.g. ftell
+ not sizeArg.getAChild*().(VariableAccess).getTarget().getAnAssignedValue() instanceof FunctionCall and
+ // exclude field or array accesses for the size arguments
+ not sizeArg.getAChild*() instanceof FieldAccess and
+ not sizeArg.getAChild*() instanceof ArrayExpr and
+ (
+ exists(int sizeArgValue, int bufferArgSize |
+ OOB::isSizeArgGreaterThanBufferSize(bufferArg, sizeArg, bufferSource, bufferArgSize,
+ sizeArgValue, ba) and
+ message =
+ "Buffer accesses offset " + sizeArgValue + " which is greater than the fixed size " +
+ bufferArgSize + " of the $@."
+ )
+ or
+ exists(int sizeArgUpperBound, int sizeMult, int bufferArgSize |
+ OOB::isSizeArgNotCheckedLessThanFixedBufferSize(bufferArg, sizeArg, bufferSource,
+ bufferArgSize, ba, sizeArgUpperBound, sizeMult) and
+ message =
+ "Buffer may access up to offset " + sizeArgUpperBound + "*" + sizeMult +
+ " which is greater than the fixed size " + bufferArgSize + " of the $@."
+ )
+ or
+ OOB::isSizeArgNotCheckedGreaterThanZero(bufferArg, sizeArg, bufferSource, ba) and
+ message = "Buffer access may be to a negative index in the buffer."
+ )
+select ba, message, bufferSource, "buffer"
diff --git a/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.md b/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.md
new file mode 100644
index 0000000000..d8554e2ef6
--- /dev/null
+++ b/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.md
@@ -0,0 +1,194 @@
+# ARR32-C: Ensure size arguments for variable length arrays are in a valid range
+
+This query implements the CERT-C rule ARR32-C:
+
+> Ensure size arguments for variable length arrays are in a valid range
+
+
+## Description
+
+Variable length arrays (VLAs), a conditionally supported language feature, are essentially the same as traditional C arrays except that they are declared with a size that is not a constant integer expression and can be declared only at block scope or function prototype scope and no linkage. When supported, a variable length array can be declared
+
+```cpp
+{ /* Block scope */
+ char vla[size];
+}
+
+```
+where the integer expression `size` and the declaration of `vla` are both evaluated at runtime. If the size argument supplied to a variable length array is not a positive integer value, the behavior is undefined. (See [undefined behavior 75](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_75).) Additionally, if the magnitude of the argument is excessive, the program may behave in an unexpected way. An attacker may be able to leverage this behavior to overwrite critical program data \[[Griffiths 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Griffiths06)\]. The programmer must ensure that size arguments to variable length arrays, especially those derived from untrusted data, are in a valid range.
+
+Because variable length arrays are a conditionally supported feature of C11, their use in portable code should be guarded by testing the value of the macro `__STDC_NO_VLA__`. Implementations that do not support variable length arrays indicate it by setting `__STDC_NO_VLA__` to the integer constant 1.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, a variable length array of size `size` is declared. The `size` is declared as `size_t` in compliance with [INT01-C. Use rsize_t or size_t for all integer values representing the size of an object](https://wiki.sei.cmu.edu/confluence/display/c/INT01-C.+Use+rsize_t+or+size_t+for+all+integer+values+representing+the+size+of+an+object).
+
+```cpp
+#include
+
+extern void do_work(int *array, size_t size);
+
+void func(size_t size) {
+ int vla[size];
+ do_work(vla, size);
+}
+
+```
+However, the value of `size` may be zero or excessive, potentially giving rise to a security [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability).
+
+## Compliant Solution
+
+This compliant solution ensures the `size` argument used to allocate `vla` is in a valid range (between 1 and a programmer-defined maximum); otherwise, it uses an algorithm that relies on dynamic memory allocation. The solution also avoids unsigned integer wrapping that, given a sufficiently large value of `size`, would cause `malloc` to allocate insufficient storage for the array.
+
+```cpp
+#include
+#include
+
+enum { MAX_ARRAY = 1024 };
+extern void do_work(int *array, size_t size);
+
+void func(size_t size) {
+ if (0 == size || SIZE_MAX / sizeof(int) < size) {
+ /* Handle error */
+ return;
+ }
+ if (size < MAX_ARRAY) {
+ int vla[size];
+ do_work(vla, size);
+ } else {
+ int *array = (int *)malloc(size * sizeof(int));
+ if (array == NULL) {
+ /* Handle error */
+ }
+ do_work(array, size);
+ free(array);
+ }
+}
+
+```
+
+## Noncompliant Code Example (sizeof)
+
+The following noncompliant code example defines `A` to be a variable length array and then uses the `sizeof` operator to compute its size at runtime. When the function is called with an argument greater than `SIZE_MAX / (N1 * sizeof (int))`, the runtime `sizeof` expression may wrap around, yielding a result that is smaller than the mathematical product `N1 * n2 * sizeof (int)`. The call to `malloc()`, when successful, will then allocate storage for fewer than `n2` elements of the array, causing one or more of the final `memset()` calls in the `for` loop to write past the end of that storage.
+
+```cpp
+#include
+#include
+
+enum { N1 = 4096 };
+
+void *func(size_t n2) {
+ typedef int A[n2][N1];
+
+ A *array = malloc(sizeof(A));
+ if (!array) {
+ /* Handle error */
+ return NULL;
+ }
+
+ for (size_t i = 0; i != n2; ++i) {
+ memset(array[i], 0, N1 * sizeof(int));
+ }
+
+ return array;
+}
+
+```
+Furthermore, this code also violates [ARR39-C. Do not add or subtract a scaled integer to a pointer](https://wiki.sei.cmu.edu/confluence/display/c/ARR39-C.+Do+not+add+or+subtract+a+scaled+integer+to+a+pointer), where `array` is a pointer to the two-dimensional array, where it should really be a pointer to the latter dimension instead. This means that the `memset() `call does out-of-bounds writes on all of its invocations except the first.
+
+## Compliant Solution (sizeof)
+
+This compliant solution prevents `sizeof` wrapping by detecting the condition before it occurs and avoiding the subsequent computation when the condition is detected. The code also uses an additional typedef to fix the type of `array` so that `memset()` never writes past the two-dimensional array.
+
+```cpp
+#include
+#include
+#include
+
+enum { N1 = 4096 };
+
+void *func(size_t n2) {
+ if (n2 > SIZE_MAX / (N1 * sizeof(int))) {
+ /* Prevent sizeof wrapping */
+ return NULL;
+ }
+
+ typedef int A1[N1];
+ typedef A1 A[n2];
+
+ A1 *array = (A1*) malloc(sizeof(A));
+
+ if (!array) {
+ /* Handle error */
+ return NULL;
+ }
+
+ for (size_t i = 0; i != n2; ++i) {
+ memset(array[i], 0, N1 * sizeof(int));
+ }
+ return array;
+}
+
+```
+**Implementation Details**
+
+**Microsoft**
+
+Variable length arrays are not supported by Microsoft compilers.
+
+## Risk Assessment
+
+Failure to properly specify the size of a variable length array may allow arbitrary code execution or result in stack exhaustion.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR32-C | High | Probable | High | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
CodeSonar | 7.2p0 | ALLOC.SIZE.IOFLOWALLOC.SIZE.MULOFLOWMISC.MEM.SIZE.BAD | Integer Overflow of Allocation Size Multiplication Overflow of Allocation Size Unreasonable Size Argument |
Coverity | 2017.07 | REVERSE_NEGATIVE | Fully implemented |
Helix QAC | 2022.4 | C1051 | |
Klocwork | 2022.4 | MISRA.ARRAY.VAR_LENGTH.2012 | |
LDRA tool suite | 9.7.1 | 621 S | Enhanced enforcement |
Parasoft C/C++test | 2022.2 | CERT_C-ARR32-a | Ensure the size of the variable length array is in valid range |
PC-lint Plus | 1.4 | 9035 | Assistance provided |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR32-C | Checks for: Memory allocation with tainted sizeemory allocation with tainted size, tainted size of variable length arrayainted size of variable length array. Rule fully covered. |
PRQA QA-C | 9.7 | 1051 | Partially implemented |
Cppcheck | 1.66 | negativeArraySize | Context sensitive analysis Will warn only if given size is negative |
TrustInSoft Analyzer | 1.38 | alloca_bounds | Exhaustively verified. |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR32-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-129 and ARR32-C**
+
+Intersection( CWE-188, EXP39-C) = Ø
+
+ARR32-C addresses specifying the size of a variable-length array (VLA). CWE-129 addresses invalid array indices, not array sizes.
+
+**CWE-758 and ARR32-C**
+
+Independent( INT34-C, INT36-C, MSC37-C, FLP32-C, EXP33-C, EXP30-C, ERR34-C, ARR32-C)
+
+CWE-758 = Union( ARR32-C, list) where list =
+
+* Undefined behavior that results from anything other than too large a VLA dimension.
+**CWE-119 and ARR32-C**
+* Intersection( CWE-119, ARR32-C) = Ø
+* ARR32-C is not about providing a valid buffer but reading/writing outside it. It is about providing an invalid buffer, or one that exhausts the stack.
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR32-C: Ensure size arguments for variable length arrays are in a valid range](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.ql b/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.ql
new file mode 100644
index 0000000000..1356777e5f
--- /dev/null
+++ b/c/cert/src/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.ql
@@ -0,0 +1,177 @@
+/**
+ * @id c/cert/variable-length-array-size-not-in-valid-range
+ * @name ARR32-C: Ensure size arguments for variable length arrays are in a valid range
+ * @description A variable-length array size that is zero, negative, overflowed, wrapped around, or
+ * excessively large may lead to undefined behaviour.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/arr32-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.Overflow
+import semmle.code.cpp.dataflow.TaintTracking
+
+/**
+ * Gets the maximum size (in bytes) a variable-length array
+ * should be to not be deemed excessively large persuant to this rule.
+ * This value has been arbitrarily chosen to be 2^16 - 1 bytes.
+ */
+private int maximumTotalVlaSize() { result = 65535 }
+
+/**
+ * Gets the base type of a pointer or array type. In the case of an array of
+ * arrays, the inner base type is returned.
+ *
+ * Copied from IncorrectPointerScalingCommon.qll.
+ */
+private Type baseType(Type t) {
+ (
+ exists(PointerType dt |
+ dt = t.getUnspecifiedType() and
+ result = dt.getBaseType().getUnspecifiedType()
+ )
+ or
+ exists(ArrayType at |
+ at = t.getUnspecifiedType() and
+ not at.getBaseType().getUnspecifiedType() instanceof ArrayType and
+ result = at.getBaseType().getUnspecifiedType()
+ )
+ or
+ exists(ArrayType at, ArrayType at2 |
+ at = t.getUnspecifiedType() and
+ at2 = at.getBaseType().getUnspecifiedType() and
+ result = baseType(at2)
+ )
+ ) and
+ // Make sure that the type has a size and that it isn't ambiguous.
+ strictcount(result.getSize()) = 1
+}
+
+/**
+ * The `SimpleRangeAnalysis` analysis over-zealously expands upper bounds of
+ * `SubExpr`s to account for potential wrapping even when no wrapping can occur.
+ *
+ * This class represents a `SubExpr` that is safe from wrapping.
+ */
+class SafeSubExprWithErroneouslyWrappedUpperBound extends SubExpr {
+ SafeSubExprWithErroneouslyWrappedUpperBound() {
+ lowerBound(this.getLeftOperand().getFullyConverted()) -
+ upperBound(this.getRightOperand().getFullyConverted()) >= 0 and
+ upperBound(this.getFullyConverted()) = exprMaxVal(this.getFullyConverted())
+ }
+
+ /**
+ * Gets the lower bound of the difference.
+ */
+ float getlowerBoundOfDifference() {
+ result =
+ lowerBound(this.getLeftOperand().getFullyConverted()) -
+ upperBound(this.getRightOperand().getFullyConverted())
+ }
+}
+
+/**
+ * Holds if `e` is an expression that is not in a valid range due to it
+ * being partially or fully derived from an overflowing arithmetic operation.
+ */
+predicate isExprTaintedByOverflowingExpr(Expr e) {
+ exists(InterestingOverflowingOperation bop |
+ // `bop` is not pre-checked to prevent overflow/wrapping
+ not bop.hasValidPreCheck() and
+ // and the destination is tainted by `bop`
+ TaintTracking::localExprTaint(bop, e.getAChild*()) and
+ // and there does not exist a post-wrapping-check before `e`
+ not exists(GuardCondition gc |
+ gc = bop.getAValidPostCheck() and
+ gc.controls(e.getBasicBlock(), _)
+ )
+ )
+}
+
+predicate getVlaSizeExprBounds(Expr e, float lower, float upper) {
+ lower = lowerBound(e) and
+ upper =
+ // upper is the smallest of either a `SubExpr` which flows to `e` and does
+ // not wrap, or the upper bound of `e` derived from the range-analysis library
+ min(float f |
+ f =
+ any(SafeSubExprWithErroneouslyWrappedUpperBound sub |
+ DataFlow::localExprFlow(sub, e)
+ |
+ sub.getlowerBoundOfDifference()
+ ) or
+ f = upperBound(e)
+ )
+}
+
+/**
+ * Holds if `e` is not bounded to a valid range, (0 .. maximumTotalVlaSize()], for
+ * a element count of an individual variable-length array dimension.
+ */
+predicate isVlaSizeExprOutOfRange(VlaDeclStmt vla, Expr e) {
+ vla.getVlaDimensionStmt(_).getDimensionExpr() = e and
+ exists(float lower, float upper |
+ getVlaSizeExprBounds(e.getFullyConverted(), lower, upper) and
+ (
+ lower <= 0
+ or
+ upper > maximumTotalVlaSize() / baseType(vla.getVariable().getType()).getSize()
+ )
+ )
+}
+
+/**
+ * Returns the upper bound of `e.getFullyConverted()`.
+ */
+float getVlaSizeExprUpperBound(Expr e) { getVlaSizeExprBounds(e.getFullyConverted(), _, result) }
+
+/**
+ * Returns the upper bound of `vla`'s dimension expression at `index`.
+ *
+ * If `index` does not exist, then the result is `1`.
+ */
+bindingset[index]
+private float getVlaSizeExprUpperBoundAtIndexOrOne(VlaDeclStmt vla, float index) {
+ if vla.getNumberOfVlaDimensionStmts() > index
+ then result = getVlaSizeExprUpperBound(vla.getVlaDimensionStmt(index).getDimensionExpr())
+ else result = 1
+}
+
+predicate vlaupper = getVlaSizeExprUpperBoundAtIndexOrOne/2;
+
+/**
+ * Gets the upper bound of the total size of `vla`.
+ */
+float getTotalVlaSizeUpperBound(VlaDeclStmt vla) {
+ result =
+ vlaupper(vla, 0) * vlaupper(vla, 1) * vlaupper(vla, 2) * vlaupper(vla, 3) * vlaupper(vla, 4) *
+ vlaupper(vla, 5) * vlaupper(vla, 6) * vlaupper(vla, 7) * vlaupper(vla, 8) * vlaupper(vla, 9)
+}
+
+from VlaDeclStmt vla, string message
+where
+ not isExcluded(vla, InvalidMemory2Package::variableLengthArraySizeNotInValidRangeQuery()) and
+ (
+ if isExprTaintedByOverflowingExpr(vla.getVlaDimensionStmt(_).getDimensionExpr())
+ then message = "Variable-length array size derives from an overflowing or wrapping expression."
+ else (
+ if isVlaSizeExprOutOfRange(vla, vla.getVlaDimensionStmt(_).getDimensionExpr())
+ then message = "Variable-length array dimension size may be in an invalid range."
+ else (
+ getTotalVlaSizeUpperBound(vla) > maximumTotalVlaSize() and
+ message = "Variable-length array total size may be excessively large."
+ )
+ )
+ )
+select vla, message
diff --git a/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.md b/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.md
new file mode 100644
index 0000000000..320eaa0c05
--- /dev/null
+++ b/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.md
@@ -0,0 +1,105 @@
+# ARR36-C: Do not subtract two pointers that do not refer to the same array
+
+This query implements the CERT-C rule ARR36-C:
+
+> Do not subtract or compare two pointers that do not refer to the same array
+
+
+## Description
+
+When two pointers are subtracted, both must point to elements of the same array object or just one past the last element of the array object (C Standard, 6.5.6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\]); the result is the difference of the subscripts of the two array elements. Otherwise, the operation is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 48](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_48).)
+
+Similarly, comparing pointers using the relational operators `<`, `<=`, `>=`, and `>` gives the positions of the pointers relative to each other. Subtracting or comparing pointers that do not refer to the same array is undefined behavior. (See [undefined behavior 48](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_48) and [undefined behavior 53](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_53).)
+
+Comparing pointers using the equality operators `==` and `!=` has well-defined semantics regardless of whether or not either of the pointers is null, points into the same object, or points one past the last element of an array object or function.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, pointer subtraction is used to determine how many free elements are left in the `nums` array:
+
+```cpp
+#include
+
+enum { SIZE = 32 };
+
+void func(void) {
+ int nums[SIZE];
+ int end;
+ int *next_num_ptr = nums;
+ size_t free_elements;
+
+ /* Increment next_num_ptr as array fills */
+
+ free_elements = &end - next_num_ptr;
+}
+```
+This program incorrectly assumes that the `nums` array is adjacent to the `end` variable in memory. A compiler is permitted to insert padding bits between these two variables or even reorder them in memory.
+
+## Compliant Solution
+
+In this compliant solution, the number of free elements is computed by subtracting `next_num_ptr` from the address of the pointer past the `nums` array. While this pointer may not be dereferenced, it may be used in pointer arithmetic.
+
+```cpp
+#include
+enum { SIZE = 32 };
+
+void func(void) {
+ int nums[SIZE];
+ int *next_num_ptr = nums;
+ size_t free_elements;
+
+ /* Increment next_num_ptr as array fills */
+
+ free_elements = &(nums[SIZE]) - next_num_ptr;
+}
+```
+
+## Exceptions
+
+**ARR36-C-EX1:**Comparing two pointers to distinct members of the same `struct` object is allowed. Pointers to structure members declared later in the structure compare greater-than pointers to members declared earlier in the structure.
+
+## Risk Assessment
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR36-C | Medium | Probable | Medium | P8 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointer-subtraction | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-ARR36 | Can detect operations on pointers that are unrelated |
CodeSonar | 7.2p0 | LANG.STRUCT.CUP LANG.STRUCT.SUP | Comparison of Unrelated Pointers Subtraction of Unrelated Pointers |
Coverity | 2017.07 | MISRA C 2004 17.2 MISRA C 2004 17.3 MISRA C 2012 18.2 MISRA C 2012 18.3 | Implemented |
Helix QAC | 2022.4 | C0487, C0513 DF2668, DF2669, DF2761, DF2762, DF2763, DF2766, DF2767, DF2768, DF2771, DF2772, DF2773 | |
Klocwork | 2022.4 | MISRA.PTR.ARITH | |
LDRA tool suite | 9.7.1 | 437 S, 438 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-ARR36-aCERT_C-ARR36-b | Do not subtract two pointers that do not address elements of the same array Do not compare two unrelated pointers |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR36-C | Checks for subtraction or comparison between pointers to different arrays (rule partially covered) |
PRQA QA-C | 9.7 | 0487, 0513, 2668, 2669, 2761, 2762, 2763, 2766, 2767, 2768, 2771, 2772, 2773 | Fully implemented |
PVS-Studio | 7.23 | V736 , V782 | |
RuleChecker | 22.04 | pointer-subtraction | Partially checked |
TrustInSoft Analyzer | 1.38 | differing_blocks | Exhaustively verified (see the compliant and the non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR36-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-469 and ARR36-C**
+
+CWE-469 = Subset(ARR36-C)
+
+ARR36-C = Union(CWE-469, list) where list =
+
+* Pointer comparisons using the relational operators `<`, `<=`, `>=`, and `>`, where the pointers do not refer to the same array
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR36-C: Do not subtract or compare two pointers that do not refer to the same array](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.ql b/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.ql
new file mode 100644
index 0000000000..e42437042f
--- /dev/null
+++ b/c/cert/src/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.ql
@@ -0,0 +1,28 @@
+/**
+ * @id c/cert/do-not-relate-pointers-that-do-not-refer-to-the-same-array
+ * @name ARR36-C: Do not subtract two pointers that do not refer to the same array
+ * @description Comparison using the >, >=, <, and <= operators between pointers referring to
+ * differing arrays results in undefined behavior.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity warning
+ * @tags external/cert/id/arr36-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.donotuserelationaloperatorswithdifferingarrays.DoNotUseRelationalOperatorsWithDifferingArrays
+
+class DoNotRelatePointersThatDoNotReferToTheSameArrayQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery
+{
+ DoNotRelatePointersThatDoNotReferToTheSameArrayQuery() {
+ this = Memory2Package::doNotRelatePointersThatDoNotReferToTheSameArrayQuery()
+ }
+}
diff --git a/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.md b/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.md
new file mode 100644
index 0000000000..320eaa0c05
--- /dev/null
+++ b/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.md
@@ -0,0 +1,105 @@
+# ARR36-C: Do not subtract two pointers that do not refer to the same array
+
+This query implements the CERT-C rule ARR36-C:
+
+> Do not subtract or compare two pointers that do not refer to the same array
+
+
+## Description
+
+When two pointers are subtracted, both must point to elements of the same array object or just one past the last element of the array object (C Standard, 6.5.6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\]); the result is the difference of the subscripts of the two array elements. Otherwise, the operation is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 48](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_48).)
+
+Similarly, comparing pointers using the relational operators `<`, `<=`, `>=`, and `>` gives the positions of the pointers relative to each other. Subtracting or comparing pointers that do not refer to the same array is undefined behavior. (See [undefined behavior 48](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_48) and [undefined behavior 53](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_53).)
+
+Comparing pointers using the equality operators `==` and `!=` has well-defined semantics regardless of whether or not either of the pointers is null, points into the same object, or points one past the last element of an array object or function.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, pointer subtraction is used to determine how many free elements are left in the `nums` array:
+
+```cpp
+#include
+
+enum { SIZE = 32 };
+
+void func(void) {
+ int nums[SIZE];
+ int end;
+ int *next_num_ptr = nums;
+ size_t free_elements;
+
+ /* Increment next_num_ptr as array fills */
+
+ free_elements = &end - next_num_ptr;
+}
+```
+This program incorrectly assumes that the `nums` array is adjacent to the `end` variable in memory. A compiler is permitted to insert padding bits between these two variables or even reorder them in memory.
+
+## Compliant Solution
+
+In this compliant solution, the number of free elements is computed by subtracting `next_num_ptr` from the address of the pointer past the `nums` array. While this pointer may not be dereferenced, it may be used in pointer arithmetic.
+
+```cpp
+#include
+enum { SIZE = 32 };
+
+void func(void) {
+ int nums[SIZE];
+ int *next_num_ptr = nums;
+ size_t free_elements;
+
+ /* Increment next_num_ptr as array fills */
+
+ free_elements = &(nums[SIZE]) - next_num_ptr;
+}
+```
+
+## Exceptions
+
+**ARR36-C-EX1:**Comparing two pointers to distinct members of the same `struct` object is allowed. Pointers to structure members declared later in the structure compare greater-than pointers to members declared earlier in the structure.
+
+## Risk Assessment
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR36-C | Medium | Probable | Medium | P8 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointer-subtraction | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-ARR36 | Can detect operations on pointers that are unrelated |
CodeSonar | 7.2p0 | LANG.STRUCT.CUP LANG.STRUCT.SUP | Comparison of Unrelated Pointers Subtraction of Unrelated Pointers |
Coverity | 2017.07 | MISRA C 2004 17.2 MISRA C 2004 17.3 MISRA C 2012 18.2 MISRA C 2012 18.3 | Implemented |
Helix QAC | 2022.4 | C0487, C0513 DF2668, DF2669, DF2761, DF2762, DF2763, DF2766, DF2767, DF2768, DF2771, DF2772, DF2773 | |
Klocwork | 2022.4 | MISRA.PTR.ARITH | |
LDRA tool suite | 9.7.1 | 437 S, 438 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-ARR36-aCERT_C-ARR36-b | Do not subtract two pointers that do not address elements of the same array Do not compare two unrelated pointers |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR36-C | Checks for subtraction or comparison between pointers to different arrays (rule partially covered) |
PRQA QA-C | 9.7 | 0487, 0513, 2668, 2669, 2761, 2762, 2763, 2766, 2767, 2768, 2771, 2772, 2773 | Fully implemented |
PVS-Studio | 7.23 | V736 , V782 | |
RuleChecker | 22.04 | pointer-subtraction | Partially checked |
TrustInSoft Analyzer | 1.38 | differing_blocks | Exhaustively verified (see the compliant and the non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR36-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-469 and ARR36-C**
+
+CWE-469 = Subset(ARR36-C)
+
+ARR36-C = Union(CWE-469, list) where list =
+
+* Pointer comparisons using the relational operators `<`, `<=`, `>=`, and `>`, where the pointers do not refer to the same array
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR36-C: Do not subtract or compare two pointers that do not refer to the same array](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.ql b/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.ql
new file mode 100644
index 0000000000..a9e53e68b7
--- /dev/null
+++ b/c/cert/src/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.ql
@@ -0,0 +1,28 @@
+/**
+ * @id c/cert/do-not-subtract-pointers-that-do-not-refer-to-the-same-array
+ * @name ARR36-C: Do not subtract two pointers that do not refer to the same array
+ * @description Subtraction between pointers referring to differing arrays results in undefined
+ * behavior.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity warning
+ * @tags external/cert/id/arr36-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.donotsubtractpointersaddressingdifferentarrays.DoNotSubtractPointersAddressingDifferentArrays
+
+class DoNotSubtractPointersThatDoNotReferToTheSameArrayQuery extends DoNotSubtractPointersAddressingDifferentArraysSharedQuery
+{
+ DoNotSubtractPointersThatDoNotReferToTheSameArrayQuery() {
+ this = Memory2Package::doNotSubtractPointersThatDoNotReferToTheSameArrayQuery()
+ }
+}
diff --git a/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.md b/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.md
new file mode 100644
index 0000000000..7772bf4d3d
--- /dev/null
+++ b/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.md
@@ -0,0 +1,148 @@
+# ARR37-C: Do not add or subtract an integer to a pointer to a non-array object
+
+This query implements the CERT-C rule ARR37-C:
+
+> Do not add or subtract an integer to a pointer to a non-array object
+
+
+## Description
+
+Pointer arithmetic must be performed only on pointers that reference elements of array objects.
+
+The C Standard, 6.5.6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states the following about pointer arithmetic:
+
+> When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression.
+
+
+## Noncompliant Code Example
+
+This noncompliant code example attempts to access structure members using pointer arithmetic. This practice is dangerous because structure members are not guaranteed to be contiguous.
+
+```cpp
+struct numbers {
+ short num_a, num_b, num_c;
+};
+
+int sum_numbers(const struct numbers *numb){
+ int total = 0;
+ const short *numb_ptr;
+
+ for (numb_ptr = &numb->num_a;
+ numb_ptr <= &numb->num_c;
+ numb_ptr++) {
+ total += *(numb_ptr);
+ }
+
+ return total;
+}
+
+int main(void) {
+ struct numbers my_numbers = { 1, 2, 3 };
+ sum_numbers(&my_numbers);
+ return 0;
+}
+
+```
+
+## Compliant Solution
+
+It is possible to use the `->` operator to dereference each structure member:
+
+```cpp
+total = numb->num_a + numb->num_b + numb->num_c;
+
+```
+However, this solution results in code that is hard to write and hard to maintain (especially if there are many more structure members), which is exactly what the author of the noncompliant code example was likely trying to avoid.
+
+## Compliant Solution
+
+A better solution is to define the structure to contain an array member to store the numbers in an array rather than a structure, as in this compliant solution:
+
+```cpp
+#include
+
+struct numbers {
+ short a[3];
+};
+
+int sum_numbers(const short *numb, size_t dim) {
+ int total = 0;
+ for (size_t i = 0; i < dim; ++i) {
+ total += numb[i];
+ }
+
+ return total;
+}
+
+int main(void) {
+ struct numbers my_numbers = { .a[0]= 1, .a[1]= 2, .a[2]= 3};
+ sum_numbers(
+ my_numbers.a,
+ sizeof(my_numbers.a)/sizeof(my_numbers.a[0])
+ );
+ return 0;
+}
+
+```
+Array elements are guaranteed to be contiguous in memory, so this solution is completely portable.
+
+## Exceptions
+
+** ARR37-C-EX1:** Any non-array object in memory can be considered an array consisting of one element. Adding one to a pointer for such an object yields a pointer one element past the array, and subtracting one from that pointer yields the original pointer. This allows for code such as the following:
+
+```cpp
+#include
+#include
+
+struct s {
+ char *c_str;
+ /* Other members */
+};
+
+struct s *create_s(const char *c_str) {
+ struct s *ret;
+ size_t len = strlen(c_str) + 1;
+
+ ret = (struct s *)malloc(sizeof(struct s) + len);
+ if (ret != NULL) {
+ ret->c_str = (char *)(ret + 1);
+ memcpy(ret + 1, c_str, len);
+ }
+ return ret;
+}
+```
+A more general and safer solution to this problem is to use a flexible array member that guarantees the array that follows the structure is properly aligned by inserting padding, if necessary, between it and the member that immediately precedes it.
+
+## Risk Assessment
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR37-C | Medium | Probable | Medium | P8 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | | Supported indirectly via MISRA C:2004 Rule 17.4. |
Axivion Bauhaus Suite | 7.2.0 | CertC-ARR37 | Fully implemented |
CodeSonar | 7.2p0 | LANG.MEM.BO LANG.MEM.BU LANG.STRUCT.PARITH LANG.STRUCT.PBB LANG.STRUCT.PPE LANG.MEM.TBA LANG.MEM.TO LANG.MEM.TU | Buffer Overrun Buffer Underrun Pointer Arithmetic Pointer Before Beginning of Object Pointer Past End of Object Tainted Buffer Access Type Overrun Type Underrun |
Compass/ROSE | | | |
Coverity | 2017.07 | ARRAY_VS_SINGLETON | Implemented |
Helix QAC | 2022.4 | DF2930, DF2931, DF2932, DF2933 C++3705, C++3706, C++3707 | |
Klocwork | 2022.4 | MISRA.PTR.ARITH.2012 | |
LDRA tool suite | 9.7.1 | 567 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-ARR37-a | Pointer arithmetic shall not be applied to pointers that address variables of non-array type |
PC-lint Plus | 1.4 | 2662 | Partially supported |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR37-C | Checks for invalid assumptions about memory organization (rule partially covered) |
PRQA QA-C | 9.7 | 2930, 2931, 2932, 2933 | |
PRQA QA-C++ | 4.4 | 2930, 2931, 2932, 2933, 3705, 3706, 3707 | |
RuleChecker | 22.04 | | Supported indirectly via MISRA C:2004 Rule 17.4. |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR37-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+ Taxonomy | Taxonomy item | Relationship |
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR37-C: Do not add or subtract an integer to a pointer to a non-array object](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql b/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql
new file mode 100644
index 0000000000..635d9d5c03
--- /dev/null
+++ b/c/cert/src/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql
@@ -0,0 +1,117 @@
+/**
+ * @id c/cert/do-not-use-pointer-arithmetic-on-non-array-object-pointers
+ * @name ARR37-C: Do not add or subtract an integer to a pointer to a non-array object
+ * @description A pair of elements that are not elements in the same array are not guaranteed to be
+ * contiguous in memory and therefore should not be addressed using pointer arithmetic.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/arr37-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.dataflow.DataFlow
+import NonArrayPointerToArrayIndexingExprFlow::PathGraph
+
+/**
+ * A data-flow configuration that tracks flow from an `AddressOfExpr` of a variable
+ * of `PointerType` that is not also an `ArrayType` to a `PointerArithmeticOrArrayExpr`
+ */
+module NonArrayPointerToArrayIndexingExprConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ exists(AddressOfExpr ao, Type t |
+ source.asExpr() = ao and
+ not ao.getOperand() instanceof ArrayExpr and
+ not ao.getOperand() instanceof PointerDereferenceExpr and
+ t = ao.getOperand().getType() and
+ not t instanceof PointerType and
+ not t instanceof ArrayType and
+ not t.(PointerType).getBaseType() instanceof ArrayType
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(PointerArithmeticOrArrayExpr ae |
+ sink.asExpr() = ae.getPointerOperand() and
+ not sink.asExpr() instanceof Literal and
+ not ae.isNonPointerOperandZero()
+ )
+ }
+
+ predicate isBarrierOut(DataFlow::Node node) {
+ // the default interprocedural data-flow model flows through any field or array assignment
+ // expressions to the qualifier (array base, pointer dereferenced, or qualifier) instead of the
+ // individual element or field that the assignment modifies. this default behaviour causes
+ // false positives for future accesses of any element of that object, so we remove the edges
+ // between those assignments from the graph with `isBarrierOut`.
+ exists(AssignExpr a |
+ node.asExpr() = a.getRValue() and
+ (
+ a.getLValue() instanceof ArrayExpr or
+ a.getLValue() instanceof PointerDereferenceExpr or
+ a.getLValue() instanceof FieldAccess
+ )
+ )
+ or
+ // ignore AddressOfExpr output e.g. call(&s1)
+ node.asDefiningArgument() instanceof AddressOfExpr
+ }
+}
+
+module NonArrayPointerToArrayIndexingExprFlow =
+ DataFlow::Global;
+
+class PointerArithmeticOrArrayExpr extends Expr {
+ Expr operand;
+
+ PointerArithmeticOrArrayExpr() {
+ operand = this.(ArrayExpr).getArrayBase()
+ or
+ operand = this.(ArrayExpr).getArrayOffset()
+ or
+ operand = this.(PointerAddExpr).getAnOperand()
+ or
+ operand = this.(PointerSubExpr).getAnOperand()
+ or
+ operand = this.(Operation).getAnOperand() and
+ operand.getUnderlyingType() instanceof PointerType and
+ (
+ this instanceof PostfixCrementOperation
+ or
+ this instanceof PrefixIncrExpr
+ or
+ this instanceof PrefixDecrExpr
+ )
+ }
+
+ /**
+ * Gets the operands of this expression. If the expression is an
+ * `ArrayExpr`, the results are the array base and offset `Expr`s.
+ */
+ Expr getPointerOperand() {
+ result = operand or
+ result = this.(PointerArithmeticOrArrayExpr).getPointerOperand()
+ }
+
+ /**
+ * Holds if there exists an operand that is a `Literal` with a value of `0`.
+ */
+ predicate isNonPointerOperandZero() { operand.(Literal).getValue().toInt() = 0 }
+}
+
+from
+ NonArrayPointerToArrayIndexingExprFlow::PathNode source,
+ NonArrayPointerToArrayIndexingExprFlow::PathNode sink
+where
+ not isExcluded(sink.getNode().asExpr(),
+ InvalidMemory2Package::doNotUsePointerArithmeticOnNonArrayObjectPointersQuery()) and
+ NonArrayPointerToArrayIndexingExprFlow::flowPath(source, sink)
+select sink, source, sink, "Pointer arithmetic on non-array object pointer."
diff --git a/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.md b/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.md
new file mode 100644
index 0000000000..c3306036d2
--- /dev/null
+++ b/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.md
@@ -0,0 +1,486 @@
+# ARR38-C: Guarantee that library functions do not form invalid pointers
+
+This query implements the CERT-C rule ARR38-C:
+
+> Guarantee that library functions do not form invalid pointers
+
+
+## Description
+
+C library functions that make changes to arrays or objects take at least two arguments: a pointer to the array or object and an integer indicating the number of elements or bytes to be manipulated. For the purposes of this rule, the element count of a pointer is the size of the object to which it points, expressed by the number of elements that are valid to access. Supplying arguments to such a function might cause the function to form a pointer that does not point into or just past the end of the object, resulting in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+Annex J of the C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\] states that it is undefined behavior if the "pointer passed to a library function array parameter does not have a value such that all address computations and object accesses are valid." (See [undefined behavior 109](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_109).)
+
+In the following code,
+
+```cpp
+int arr[5];
+int *p = arr;
+
+unsigned char *p2 = (unsigned char *)arr;
+unsigned char *p3 = arr + 2;
+void *p4 = arr;
+```
+the element count of the pointer `p` is `sizeof(arr) / sizeof(arr[0])`, that is, `5`. The element count of the pointer `p2` is `sizeof(arr)`, that is, `20`, on implementations where `sizeof(int) == 4`. The element count of the pointer `p3` is `12` on implementations where `sizeof(int) == 4`, because `p3` points two elements past the start of the array `arr`. The element count of `p4` is treated as though it were `unsigned char *` instead of `void *`, so it is the same as `p2`.
+
+## Pointer + Integer
+
+The following standard library functions take a pointer argument and a size argument, with the constraint that the pointer must point to a valid memory object of at least the number of elements indicated by the size argument.
+
+ fgets() | fgetws() | mbstowcs() 1 | wcstombs() 1 |
mbrtoc16() 2 | mbrtoc32() 2 | mbsrtowcs() 1 | wcsrtombs() 1 |
mbtowc() 2 | mbrtowc() 2 | mblen() | mbrlen() |
memchr() | wmemchr() | memset() | wmemset() |
strftime() | wcsftime() | strxfrm()1 | wcsxfrm()1 |
strncat()2 | wcsncat()2 | snprintf() | vsnprintf() |
swprintf() | vswprintf() | setvbuf() | tmpnam_s() |
snprintf_s() | sprintf_s() | vsnprintf_s() | vsprintf_s() |
gets_s() | getenv_s() | wctomb_s() | mbstowcs_s()3 |
wcstombs_s()3 | memcpy_s()3 | memmove_s()3 | strncpy_s()3 |
strncat_s()3 | strtok_s()2 | strerror_s() | strnlen_s() |
asctime_s() | ctime_s() | snwprintf_s() | swprintf_s() |
vsnwprintf_s() | vswprintf_s() | wcsncpy_s()3 | wmemcpy_s()3 |
wmemmove_s()3 | wcsncat_s()3 | wcstok_s()2 | wcsnlen_s() |
wcrtomb_s() | mbsrtowcs_s()3 | wcsrtombs_s()3 | memset_s()4 |
+1 Takes two pointers and an integer, but the integer specifies the element count only of the output buffer, not of the input buffer.2 Takes two pointers and an integer, but the integer specifies the element count only of the input buffer, not of the output buffer.3 Takes two pointers and two integers; each integer corresponds to the element count of one of the pointers.4 Takes a pointer and two size-related integers; the first size-related integer parameter specifies the number of bytes available in the buffer; the second size-related integer parameter specifies the number of bytes to write within the buffer.
+
+
+For calls that take a pointer and an integer size, the given size should not be greater than the element count of the pointer.
+
+** Noncompliant Code Example (Element Count)**
+
+In this noncompliant code example, the incorrect element count is used in a call to `wmemcpy()`. The `sizeof` operator returns the size expressed in bytes, but `wmemcpy()` uses an element count based on `wchar_t *`.
+
+```cpp
+#include
+#include
+
+static const char str[] = "Hello world";
+static const wchar_t w_str[] = L"Hello world";
+void func(void) {
+ char buffer[32];
+ wchar_t w_buffer[32];
+ memcpy(buffer, str, sizeof(str)); /* Compliant */
+ wmemcpy(w_buffer, w_str, sizeof(w_str)); /* Noncompliant */
+}
+```
+**Compliant Solution (Element Count)**
+
+When using functions that operate on pointed-to regions, programmers must always express the integer size in terms of the element count expected by the function. For example, `memcpy()` expects the element count expressed in terms of `void *`, but `wmemcpy()` expects the element count expressed in terms of `wchar_t *`. Instead of the `sizeof` operator, functions that return the number of elements in the string are called, which matches the expected element count for the copy functions. In the case of this compliant solution, where the argument is an array `A` of type `T`, the expression `sizeof(A) / sizeof(T)`, or equivalently `sizeof(A) / sizeof(*A)`, can be used to compute the number of elements in the array.
+
+```cpp
+#include
+#include
+
+static const char str[] = "Hello world";
+static const wchar_t w_str[] = L"Hello world";
+void func(void) {
+ char buffer[32];
+ wchar_t w_buffer[32];
+ memcpy(buffer, str, strlen(str) + 1);
+ wmemcpy(w_buffer, w_str, wcslen(w_str) + 1);
+}
+```
+**Noncompliant Code Example (Pointer + Integer)**
+
+This noncompliant code example assigns a value greater than the number of bytes of available memory to `n`, which is then passed to `memset()`:
+
+```cpp
+#include
+#include
+
+void f1(size_t nchars) {
+ char *p = (char *)malloc(nchars);
+ /* ... */
+ const size_t n = nchars + 1;
+ /* ... */
+ memset(p, 0, n);
+}
+
+```
+**Compliant Solution (Pointer + Integer)**
+
+This compliant solution ensures that the value of `n` is not greater than the number of bytes of the dynamic memory pointed to by the pointer `p`:
+
+```cpp
+#include
+#include
+
+void f1(size_t nchars) {
+ char *p = (char *)malloc(nchars);
+ /* ... */
+ const size_t n = nchars;
+ /* ... */
+ memset(p, 0, n);
+}
+
+```
+**Noncompliant Code Example (Pointer + Integer)**
+
+In this noncompliant code example, the element count of the array `a` is `ARR_SIZE` elements. Because `memset()` expects a byte count, the size of the array is scaled incorrectly by `sizeof(int)` instead of `sizeof(long)`, which can form an invalid pointer on architectures where `sizeof(int) != sizeof(long)`.
+
+```cpp
+#include
+
+void f2(void) {
+ const size_t ARR_SIZE = 4;
+ long a[ARR_SIZE];
+ const size_t n = sizeof(int) * ARR_SIZE;
+ void *p = a;
+
+ memset(p, 0, n);
+}
+
+```
+**Compliant Solution (Pointer + Integer)**
+
+In this compliant solution, the element count required by `memset()` is properly calculated without resorting to scaling:
+
+```cpp
+#include
+
+void f2(void) {
+ const size_t ARR_SIZE = 4;
+ long a[ARR_SIZE];
+ const size_t n = sizeof(a);
+ void *p = a;
+
+ memset(p, 0, n);
+}
+
+```
+
+## Two Pointers + One Integer
+
+The following standard library functions take two pointer arguments and a size argument, with the constraint that both pointers must point to valid memory objects of at least the number of elements indicated by the size argument.
+
+ memcpy() | wmemcpy() | memmove() | wmemmove() |
strncpy() | wcsncpy() | memcmp() | wmemcmp() |
strncmp() | wcsncmp() | strcpy_s() | wcscpy_s() |
strcat_s() | wcscat_s() | | |
+For calls that take two pointers and an integer size, the given size should not be greater than the element count of either pointer.
+
+
+**Noncompliant Code Example (Two Pointers + One Integer)**
+
+In this noncompliant code example, the value of `n` is incorrectly computed, allowing a read past the end of the object referenced by `q`:
+
+```cpp
+#include
+
+void f4() {
+ char p[40];
+ const char *q = "Too short";
+ size_t n = sizeof(p);
+ memcpy(p, q, n);
+}
+```
+**Compliant Solution (Two Pointers + One Integer)**
+
+This compliant solution ensures that `n` is equal to the size of the character array:
+
+```cpp
+#include
+
+void f4() {
+ char p[40];
+ const char *q = "Too short";
+ size_t n = sizeof(p) < strlen(q) + 1 ? sizeof(p) : strlen(q) + 1;
+ memcpy(p, q, n);
+}
+```
+
+## One Pointer + Two Integers
+
+The following standard library functions take a pointer argument and two size arguments, with the constraint that the pointer must point to a valid memory object containing at least as many bytes as the product of the two size arguments.
+
+ bsearch() | bsearch_s() | qsort() | qsort_s() |
fread() | fwrite() | | |
+For calls that take a pointer and two integers, one integer represents the number of bytes required for an individual object, and a second integer represents the number of elements in the array. The resulting product of the two integers should not be greater than the element count of the pointer were it expressed as an `unsigned char *`.
+
+
+**Noncompliant Code Example (One Pointer + Two Integers)**
+
+This noncompliant code example allocates a variable number of objects of type `struct obj`. The function checks that `num_objs` is small enough to prevent wrapping, in compliance with [INT30-C. Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap). The size of `struct obj` is assumed to be 16 bytes to account for padding to achieve the assumed alignment of `long long`. However, the padding typically depends on the target architecture, so this object size may be incorrect, resulting in an incorrect element count.
+
+```cpp
+#include
+#include
+
+struct obj {
+ char c;
+ long long i;
+};
+
+void func(FILE *f, struct obj *objs, size_t num_objs) {
+ const size_t obj_size = 16;
+ if (num_objs > (SIZE_MAX / obj_size) ||
+ num_objs != fwrite(objs, obj_size, num_objs, f)) {
+ /* Handle error */
+ }
+}
+```
+**Compliant Solution (One Pointer + Two Integers)**
+
+This compliant solution uses the `sizeof` operator to correctly provide the object size and `num_objs` to provide the element count:
+
+```cpp
+#include
+#include
+
+struct obj {
+ char c;
+ long long i;
+};
+
+void func(FILE *f, struct obj *objs, size_t num_objs) {
+ const size_t obj_size = sizeof *objs;
+ if (num_objs > (SIZE_MAX / obj_size) ||
+ num_objs != fwrite(objs, obj_size, num_objs, f)) {
+ /* Handle error */
+ }
+}
+```
+**Noncompliant Code Example (One Pointer + Two Integers)**
+
+In this noncompliant code example, the function `f()` calls `fread()` to read `nitems` of type `wchar_t`, each `size` bytes in size, into an array of `BUFFER_SIZE` elements, `wbuf`. However, the expression used to compute the value of `nitems` fails to account for the fact that, unlike the size of `char`, the size of `wchar_t` may be greater than 1. Consequently, `fread()` could attempt to form pointers past the end of `wbuf` and use them to assign values to nonexistent elements of the array. Such an attempt is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 109](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_109).) A likely consequence of this undefined behavior is a buffer overflow. For a discussion of this programming error in the Common Weakness Enumeration database, see [CWE-121](http://cwe.mitre.org/data/definitions/121.html), "Stack-based Buffer Overflow," and [CWE-805](http://cwe.mitre.org/data/definitions/805.html), "Buffer Access with Incorrect Length Value."
+
+```cpp
+#include
+#include
+
+void f(FILE *file) {
+ enum { BUFFER_SIZE = 1024 };
+ wchar_t wbuf[BUFFER_SIZE];
+
+ const size_t size = sizeof(*wbuf);
+ const size_t nitems = sizeof(wbuf);
+
+ size_t nread = fread(wbuf, size, nitems, file);
+ /* ... */
+}
+
+```
+**Compliant Solution (One Pointer + Two Integers)**
+
+This compliant solution correctly computes the maximum number of items for `fread()` to read from the file:
+
+```cpp
+#include
+#include
+
+void f(FILE *file) {
+ enum { BUFFER_SIZE = 1024 };
+ wchar_t wbuf[BUFFER_SIZE];
+
+ const size_t size = sizeof(*wbuf);
+ const size_t nitems = sizeof(wbuf) / size;
+
+ size_t nread = fread(wbuf, size, nitems, file);
+ /* ... */
+}
+```
+**Noncompliant Code Example (Heartbleed)**
+
+CERT vulnerability [720951](http://www.kb.cert.org/vuls/id/720951) describes a vulnerability in OpenSSL versions 1.0.1 through 1.0.1f, popularly known as "Heartbleed." This vulnerability allows an attacker to steal information that under normal conditions would be protected by Secure Socket Layer/Transport Layer Security (SSL/TLS) encryption.
+
+Despite the seriousness of the vulnerability, Heartbleed is the result of a common programming error and an apparent lack of awareness of secure coding principles. Following is the vulnerable code:
+
+```cpp
+int dtls1_process_heartbeat(SSL *s) {
+ unsigned char *p = &s->s3->rrec.data[0], *pl;
+ unsigned short hbtype;
+ unsigned int payload;
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* Read type and payload length first */
+ hbtype = *p++;
+ n2s(p, payload);
+ pl = p;
+
+ /* ... More code ... */
+
+ if (hbtype == TLS1_HB_REQUEST) {
+ unsigned char *buffer, *bp;
+ int r;
+
+ /*
+ * Allocate memory for the response; size is 1 byte
+ * message type, plus 2 bytes payload length, plus
+ * payload, plus padding.
+ */
+ buffer = OPENSSL_malloc(1 + 2 + payload + padding);
+ bp = buffer;
+
+ /* Enter response type, length, and copy payload */
+ *bp++ = TLS1_HB_RESPONSE;
+ s2n(payload, bp);
+ memcpy(bp, pl, payload);
+
+ /* ... More code ... */
+ }
+ /* ... More code ... */
+}
+```
+This code processes a "heartbeat" packet from a client. As specified in [RFC 6520](https://tools.ietf.org/html/rfc6520), when the program receives a heartbeat packet, it must echo the packet's data back to the client. In addition to the data, the packet contains a length field that conventionally indicates the number of bytes in the packet data, but there is nothing to prevent a malicious packet from lying about its data length.
+
+The `p` pointer, along with `payload` and `p1`, contains data from a packet. The code allocates a `buffer` sufficient to contain `payload` bytes, with some overhead, then copies `payload` bytes starting at `p1` into this buffer and sends it to the client. Notably absent from this code are any checks that the payload integer variable extracted from the heartbeat packet corresponds to the size of the packet data. Because the client can specify an arbitrary value of `payload`, an attacker can cause the server to read and return the contents of memory beyond the end of the packet data, which violates [INT04-C. Enforce limits on integer values originating from tainted sources](https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources). The resulting call to `memcpy()` can then copy the contents of memory past the end of the packet data and the packet itself, potentially exposing sensitive data to the attacker. This call to `memcpy()` violates [ARR38-C. Guarantee that library functions do not form invalid pointers](https://wiki.sei.cmu.edu/confluence/display/c/ARR38-C.+Guarantee+that+library+functions+do+not+form+invalid+pointers). A version of ARR38-C also appears in [ISO/IEC TS 17961:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IECTS17961), "Forming invalid pointers by library functions \[libptr\]." This rule would require a conforming analyzer to diagnose the Heartbleed vulnerability.
+
+**Compliant Solution (Heartbleed)**
+
+OpenSSL version 1.0.1g contains the following patch, which guarantees that `payload` is within a valid range. The range is limited by the size of the input record.
+
+```cpp
+int dtls1_process_heartbeat(SSL *s) {
+ unsigned char *p = &s->s3->rrec.data[0], *pl;
+ unsigned short hbtype;
+ unsigned int payload;
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* ... More code ... */
+
+ /* Read type and payload length first */
+ if (1 + 2 + 16 > s->s3->rrec.length)
+ return 0; /* Silently discard */
+ hbtype = *p++;
+ n2s(p, payload);
+ if (1 + 2 + payload + 16 > s->s3->rrec.length)
+ return 0; /* Silently discard per RFC 6520 */
+ pl = p;
+
+ /* ... More code ... */
+
+ if (hbtype == TLS1_HB_REQUEST) {
+ unsigned char *buffer, *bp;
+ int r;
+
+ /*
+ * Allocate memory for the response; size is 1 byte
+ * message type, plus 2 bytes payload length, plus
+ * payload, plus padding.
+ */
+ buffer = OPENSSL_malloc(1 + 2 + payload + padding);
+ bp = buffer;
+ /* Enter response type, length, and copy payload */
+ *bp++ = TLS1_HB_RESPONSE;
+ s2n(payload, bp);
+ memcpy(bp, pl, payload);
+ /* ... More code ... */
+ }
+ /* ... More code ... */
+}
+```
+
+## Risk Assessment
+
+Depending on the library function called, an attacker may be able to use a heap or stack overflow [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) to run arbitrary code.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
ARR38-C | High | Likely | Medium | P18 | L1 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | array_out_of_bounds | Supported Astrée reports all out-of-bound accesses within library analysis stubs. The user may provide additional stubs for arbitrary (library) functions. |
CodeSonar | 7.2p0 | LANG.MEM.BO LANG.MEM.BU BADFUNC.BO.\* | Buffer overrun Buffer underrun A collection of warning classes that report uses of library functions prone to internal buffer overflows |
Compass/ROSE | | | |
Coverity | 2017.07 | BUFFER_SIZE BAD_SIZEOF BAD_ALLOC_STRLEN BAD_ALLOC_ARITHMETIC | Implemented |
Fortify SCA | 5.0 | | Can detect violations of this rule with CERT C Rule Pack |
Helix QAC | 2022.4 | C2840 DF2840, DF2841, DF2842, DF2843, DF2845, DF2846, DF2847, DF2848, DF2935, DF2936, DF2937, DF2938, DF4880, DF4881, DF4882, DF4883 | |
Klocwork | 2022.4 | ABV.GENERALABV.GENERAL.MULTIDIMENSION | |
LDRA tool suite | 9.7.1 | 64 X, 66 X, 68 X, 69 X, 70 X, 71 X, 79 X | Partially Implmented |
Parasoft C/C++test | 2022.2 | CERT_C-ARR38-a CERT_C-ARR38-b CERT_C-ARR38-c CERT_C-ARR38-d | Avoid overflow when reading from a buffer Avoid overflow when writing to a buffer Avoid buffer overflow due to defining incorrect format limits Avoid overflow due to reading a not zero terminated string |
Parasoft Insure++ | | | Runtime analysis |
PC-lint Plus | 1.4 | 419, 420 | Partially supported |
Polyspace Bug Finder | R2023a | CERT C: Rule ARR38-C | Checks for: Mismatch between data length and sizeismatch between data length and size, invalid use of standard library memory routinenvalid use of standard library memory routine, possible misuse of sizeofossible misuse of sizeof, buffer overflow from incorrect string format specifieruffer overflow from incorrect string format specifier, invalid use of standard library string routinenvalid use of standard library string routine, destination buffer overflow in string manipulationestination buffer overflow in string manipulation, destination buffer underflow in string manipulationestination buffer underflow in string manipulation. Rule partially covered. |
PRQA QA-C | 9.7 | 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2935, 2936, 2937, 2938 | Fully implemented |
PRQA QA-C++ | 4.4 | 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2935, 2936, 2937, 2938 | Fully implemented |
Splint | 3.1.1 | | |
TrustInSoft Analyzer | 1.38 | out of bounds read | Partially verified. |
+
+
+## Related Vulnerabilities
+
+[CVE-2016-2208](https://bugs.chromium.org/p/project-zero/issues/detail?id=820) results from a violation of this rule. The attacker can supply a value used to determine how much data is copied into a buffer via `memcpy()`, resulting in a buffer overlow of attacker-controlled data.
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ARR38-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-121 and ARR38-C**
+
+Intersection( CWE-121, ARR38-C) =
+
+* Stack buffer overflow from passing invalid arguments to library function
+CWE-121 – ARR38-C =
+* Stack buffer overflows from direct out-of-bounds write
+ARR38-C – CWE-121 =
+* Out-of-bounds read from passing invalid arguments to library function
+* Buffer overflow on heap or data segment from passing invalid arguments to library function
+**CWE-119 and ARR38-C**
+
+See CWE-119 and ARR30-C
+
+**CWE-125 and ARR38-C**
+
+Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C)
+
+STR31-C = Subset( Union( ARR30-C, ARR38-C))
+
+STR32-C = Subset( ARR38-C)
+
+Intersection( ARR38-C, CWE-125) =
+
+* Reading from an out-of-bounds array index or off the end of an array via standard library function
+ARR38-C – CWE-125 =
+* Writing to an out-of-bounds array index or off the end of an array via standard library function
+CWE-125 – ARR38-C =
+* Reading beyond a non-array buffer
+* Reading beyond an array directly (using pointer arithmetic, or \[\] notation)
+**CWE-805 and ARR38-C**
+
+Intersection( CWE-805, ARR38-C) =
+
+* Buffer access with incorrect length via passing invalid arguments to library function
+CWE-805 – ARR38-C =
+* Buffer access with incorrect length directly (such as a loop construct)
+ARR38-C – CWE-805 =
+* Out-of-bounds read or write that does not involve incorrect length (could use incorrect offset instead), that uses library function
+**CWE-123 and ARR38-C**
+
+Independent(ARR30-C, ARR38-C)
+
+STR31-C = Subset( Union( ARR30-C, ARR38-C))
+
+STR32-C = Subset( ARR38-C)
+
+CWE-123 includes any operation that allows an attacker to write an arbitrary value to an arbitrary memory location. This could be accomplished via overwriting a pointer with data that refers to the address to write, then when the program writes to a pointed-to value, supplying a malicious value. Vulnerable pointer values can be corrupted by:
+
+* Stack return address
+* Buffer overflow on the heap (which typically overwrites back/next pointer values)
+* Write to untrusted array index (if it is also invalid)
+* Format string exploit
+* Overwriting a C++ object with virtual functions (because it has a virtual pointer)
+* Others?
+Intersection( CWE-123, ARR38-C) =
+* Buffer overflow via passing invalid arguments to library function
+ARR38-C – CWE-123 =
+* Buffer overflow to “harmless” memory from passing invalid arguments to library function
+* Out-of-bounds read from passing invalid arguments to library function
+CWE-123 – ARR38-C =
+* Arbitrary writes that do not involve standard C library functions
+**CWE-129 and ARR38-C**
+
+ARR38-C - CWE-129 = making library functions create invalid pointers without using untrusted data.
+
+E.g. : `char[3] array;`
+
+`strcpy(array, "123456");`
+
+CWE-129 - ARR38-C = not validating an integer used as an array index or in pointer arithmetic
+
+E.g.: `void foo(int i) {`
+
+` char array[3];`
+
+` array[i];`
+
+`}`
+
+Intersection(ARR38-C, CWE-129) = making library functions create invalid pointers using untrusted data.
+
+`eg: void foo(int i) {`
+
+` char src[3], dest[3];`
+
+` memcpy(dest, src, i);`
+
+`}`
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [ARR38-C: Guarantee that library functions do not form invalid pointers](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.ql b/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.ql
new file mode 100644
index 0000000000..04e1c8a505
--- /dev/null
+++ b/c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.ql
@@ -0,0 +1,30 @@
+/**
+ * @id c/cert/library-function-argument-out-of-bounds
+ * @name ARR38-C: Guarantee that library functions do not form invalid pointers
+ * @description Passing out-of-bounds pointers or erroneous size arguments to standard library
+ * functions can result in out-of-bounds accesses and other undefined behavior.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/arr38-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p18
+ * external/cert/level/l1
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.c.OutOfBounds
+
+from
+ OOB::BufferAccessLibraryFunctionCall fc, string message, Expr bufferArg, string bufferArgStr,
+ Expr sizeOrOtherBufferArg, string otherStr
+where
+ not isExcluded(fc, OutOfBoundsPackage::libraryFunctionArgumentOutOfBoundsQuery()) and
+ OOB::problems(fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr)
+select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr
diff --git a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql
index 5ad9fc7f6e..c3ebd6ede6 100644
--- a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql
+++ b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql
@@ -8,54 +8,19 @@
* @problem.severity error
* @tags external/cert/id/arr39-c
* correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
+import codingstandards.cpp.types.Pointers
import semmle.code.cpp.dataflow.TaintTracking
-import DataFlow::PathGraph
-
-/**
- * An expression which performs pointer arithmetic
- */
-abstract class PointerArithmeticExpr extends Expr {
- abstract Expr getPointer();
-
- abstract Expr getOperand();
-}
-
-/**
- * A pointer arithmetic binary operation expression.
- */
-class SimplePointerArithmeticExpr extends PointerArithmeticExpr, PointerArithmeticOperation {
- override Expr getPointer() { result = this.getLeftOperand() }
-
- override Expr getOperand() { result = this.getRightOperand() }
-}
-
-/**
- * A pointer arithmetic assignment expression.
- */
-class AssignPointerArithmeticExpr extends PointerArithmeticExpr, AssignOperation {
- AssignPointerArithmeticExpr() {
- this instanceof AssignPointerAddExpr or
- this instanceof AssignPointerSubExpr
- }
-
- override Expr getPointer() { result = this.getLValue() }
-
- override Expr getOperand() { result = this.getRValue() }
-}
-
-/**
- * A pointer arithmetic array access expression.
- */
-class ArrayPointerArithmeticExpr extends PointerArithmeticExpr, ArrayExpr {
- override Expr getPointer() { result = this.getArrayBase() }
-
- override Expr getOperand() { result = this.getArrayOffset() }
-}
+import ScaledIntegerPointerArithmeticFlow::PathGraph
/**
* An expression which invokes the `offsetof` macro or `__builtin_offsetof` operation.
@@ -109,12 +74,10 @@ class ScaledIntegerExpr extends Expr {
* A data-flow configuration modeling data-flow from a `ScaledIntegerExpr` to a
* `PointerArithmeticExpr` where the pointer does not point to a 1-byte type.
*/
-class ScaledIntegerPointerArithmeticConfig extends DataFlow::Configuration {
- ScaledIntegerPointerArithmeticConfig() { this = "ScaledIntegerPointerArithmeticConfig" }
-
- override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ScaledIntegerExpr }
+module ScaledIntegerPointerArithmeticConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ScaledIntegerExpr }
- override predicate isSink(DataFlow::Node sink) {
+ predicate isSink(DataFlow::Node sink) {
exists(PointerArithmeticExpr pa |
// exclude pointers to 1-byte types as they do not scale
pa.getPointer().getFullyConverted().getType().(DerivedType).getBaseType().getSize() != 1 and
@@ -123,9 +86,13 @@ class ScaledIntegerPointerArithmeticConfig extends DataFlow::Configuration {
}
}
-from ScaledIntegerPointerArithmeticConfig config, DataFlow::PathNode src, DataFlow::PathNode sink
+module ScaledIntegerPointerArithmeticFlow = DataFlow::Global;
+
+from
+ ScaledIntegerPointerArithmeticFlow::PathNode src,
+ ScaledIntegerPointerArithmeticFlow::PathNode sink
where
not isExcluded(sink.getNode().asExpr(),
Pointers2Package::doNotAddOrSubtractAScaledIntegerToAPointerQuery()) and
- config.hasFlowPath(src, sink)
+ ScaledIntegerPointerArithmeticFlow::flowPath(src, sink)
select sink, src, sink, "Scaled integer used in pointer arithmetic."
diff --git a/c/cert/src/rules/CON30-C/CleanUpThreadSpecificStorage.ql b/c/cert/src/rules/CON30-C/CleanUpThreadSpecificStorage.ql
index 55f4afe7d8..1e03c089e8 100644
--- a/c/cert/src/rules/CON30-C/CleanUpThreadSpecificStorage.ql
+++ b/c/cert/src/rules/CON30-C/CleanUpThreadSpecificStorage.ql
@@ -9,19 +9,21 @@
* @tags external/cert/id/con30-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.Concurrency
-import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.DataFlow
-class TssCreateToTssDeleteDataFlowConfiguration extends DataFlow::Configuration {
- TssCreateToTssDeleteDataFlowConfiguration() { this = "TssCreateToTssDeleteDataFlowConfiguration" }
-
- override predicate isSource(DataFlow::Node node) {
+module TssCreateToTssDeleteConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) {
exists(TSSCreateFunctionCall tsc, Expr e |
// the only requirement of the source is that at some point
// it refers to the key of a create statement
@@ -30,7 +32,7 @@ class TssCreateToTssDeleteDataFlowConfiguration extends DataFlow::Configuration
)
}
- override predicate isSink(DataFlow::Node node) {
+ predicate isSink(DataFlow::Node node) {
exists(TSSDeleteFunctionCall tsd, Expr e |
// the only requirement of a sink is that at some point
// it references the key of a delete call.
@@ -40,15 +42,17 @@ class TssCreateToTssDeleteDataFlowConfiguration extends DataFlow::Configuration
}
}
+module TssCreateToTssDeleteFlow = DataFlow::Global;
+
from TSSCreateFunctionCall tcfc
where
not isExcluded(tcfc, Concurrency4Package::cleanUpThreadSpecificStorageQuery()) and
// all calls to `tss_create` must be bookended by calls to tss_delete
// even if a thread is not created.
- not exists(TssCreateToTssDeleteDataFlowConfiguration config |
- config.hasFlow(DataFlow::definitionByReferenceNodeFromArgument(tcfc.getKey()), _)
+ not (
+ TssCreateToTssDeleteFlow::flow(DataFlow::definitionByReferenceNodeFromArgument(tcfc.getKey()), _)
or
- config.hasFlow(DataFlow::exprNode(tcfc.getKey()), _)
+ TssCreateToTssDeleteFlow::flow(DataFlow::exprNode(tcfc.getKey()), _)
)
or
// if a thread is created, we must check additional items
diff --git a/c/cert/src/rules/CON31-C/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql b/c/cert/src/rules/CON31-C/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql
index 4e32b6eb15..345623fe0d 100644
--- a/c/cert/src/rules/CON31-C/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql
+++ b/c/cert/src/rules/CON31-C/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con31-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -16,7 +21,8 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.rules.donotallowamutextogooutofscopewhilelocked.DoNotAllowAMutexToGoOutOfScopeWhileLocked
-class DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery {
+class DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery
+{
DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery() {
this = Concurrency3Package::doNotAllowAMutexToGoOutOfScopeWhileLockedQuery()
}
diff --git a/c/cert/src/rules/CON31-C/DoNotDestroyAMutexWhileItIsLocked.ql b/c/cert/src/rules/CON31-C/DoNotDestroyAMutexWhileItIsLocked.ql
index b37dccab3a..40c4e936dd 100644
--- a/c/cert/src/rules/CON31-C/DoNotDestroyAMutexWhileItIsLocked.ql
+++ b/c/cert/src/rules/CON31-C/DoNotDestroyAMutexWhileItIsLocked.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/con31-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/CON32-C/PreventDataRacesWithMultipleThreads.ql b/c/cert/src/rules/CON32-C/PreventDataRacesWithMultipleThreads.ql
index d4f3cbbe10..3ea9e1e1fd 100644
--- a/c/cert/src/rules/CON32-C/PreventDataRacesWithMultipleThreads.ql
+++ b/c/cert/src/rules/CON32-C/PreventDataRacesWithMultipleThreads.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con32-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/CON33-C/RaceConditionsWhenUsingLibraryFunctions.ql b/c/cert/src/rules/CON33-C/RaceConditionsWhenUsingLibraryFunctions.ql
index ff9f0884d7..c9bcaa6bd2 100644
--- a/c/cert/src/rules/CON33-C/RaceConditionsWhenUsingLibraryFunctions.ql
+++ b/c/cert/src/rules/CON33-C/RaceConditionsWhenUsingLibraryFunctions.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/con33-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -24,5 +29,5 @@ where
"setlocale", "atomic_init", "ATOMIC_VAR_INIT", "tmpnam", "mbrtoc16", "c16rtomb", "mbrtoc32",
"c32rtomb"
]
-select node,
- "Concurrent call to non-reeantrant function $@.", node.(FunctionCall).getTarget(), node.(FunctionCall).getTarget().getName()
+select node, "Concurrent call to non-reeantrant function $@.", node.(FunctionCall).getTarget(),
+ node.(FunctionCall).getTarget().getName()
diff --git a/c/cert/src/rules/CON34-C/AppropriateThreadObjectStorageDurations.ql b/c/cert/src/rules/CON34-C/AppropriateThreadObjectStorageDurations.ql
index 71138f4ff8..4fb034406b 100644
--- a/c/cert/src/rules/CON34-C/AppropriateThreadObjectStorageDurations.ql
+++ b/c/cert/src/rules/CON34-C/AppropriateThreadObjectStorageDurations.ql
@@ -9,35 +9,53 @@
* @tags external/cert/id/con34-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
+import codingstandards.c.Objects
import codingstandards.cpp.Concurrency
-import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.commons.Alloc
-from C11ThreadCreateCall tcc, StackVariable sv, Expr arg, Expr acc
+from C11ThreadCreateCall tcc, Expr arg
where
not isExcluded(tcc, Concurrency4Package::appropriateThreadObjectStorageDurationsQuery()) and
tcc.getArgument(2) = arg and
- sv.getAnAccess() = acc and
- // a stack variable that is given as an argument to a thread
- TaintTracking::localTaint(DataFlow::exprNode(acc), DataFlow::exprNode(arg)) and
- // or isn't one of the allowed usage patterns
- not exists(Expr mfc |
- isAllocationExpr(mfc) and
- sv.getAnAssignedValue() = mfc and
- acc.getAPredecessor*() = mfc
- ) and
- not exists(TSSGetFunctionCall tsg, TSSSetFunctionCall tss, DataFlow::Node src |
- sv.getAnAssignedValue() = tsg and
- acc.getAPredecessor*() = tsg and
- // there should be dataflow from somewhere (the same somewhere)
- // into each of the first arguments
- DataFlow::localFlow(src, DataFlow::exprNode(tsg.getArgument(0))) and
- DataFlow::localFlow(src, DataFlow::exprNode(tss.getArgument(0)))
+ (
+ exists(ObjectIdentity obj, Expr acc |
+ obj.getASubobjectAccess() = acc and
+ obj.getStorageDuration().isAutomatic() and
+ exists(DataFlow::Node addrNode |
+ (
+ addrNode = DataFlow::exprNode(any(AddressOfExpr e | e.getOperand() = acc))
+ or
+ addrNode = DataFlow::exprNode(acc) and
+ exists(ArrayToPointerConversion c | c.getExpr() = acc)
+ ) and
+ TaintTracking::localTaint(addrNode, DataFlow::exprNode(arg))
+ )
+ )
+ or
+ // TODO: This case is handling threadlocals in a useful way that's not intended to be covered
+ // by the rule. See issue #801. The actual rule should expect no tss_t objects is used, and
+ // this check that this is initialized doesn't seem to belong here. However, it is a useful
+ // check in and of itself, so we should figure out if this is part of an optional rule we
+ // haven't yet implemented and move this behavior there.
+ exists(TSSGetFunctionCall tsg |
+ TaintTracking::localTaint(DataFlow::exprNode(tsg), DataFlow::exprNode(arg)) and
+ not exists(TSSSetFunctionCall tss, DataFlow::Node src |
+ // there should be dataflow from somewhere (the same somewhere)
+ // into each of the first arguments
+ DataFlow::localFlow(src, DataFlow::exprNode(tsg.getArgument(0))) and
+ DataFlow::localFlow(src, DataFlow::exprNode(tss.getArgument(0)))
+ )
+ )
)
select tcc, "$@ not declared with appropriate storage duration", arg, "Shared object"
diff --git a/c/cert/src/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.ql b/c/cert/src/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.ql
index ddcddb8dc5..07b114d6ca 100644
--- a/c/cert/src/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.ql
+++ b/c/cert/src/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.ql
@@ -10,13 +10,17 @@
* external/cert/audit
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.Concurrency
-import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.DataFlow
from TSSGetFunctionCall tsg, ThreadedFunction tf
diff --git a/c/cert/src/rules/CON35-C/DeadlockByLockingInPredefinedOrder.ql b/c/cert/src/rules/CON35-C/DeadlockByLockingInPredefinedOrder.ql
index 00fa021878..764b0f263f 100644
--- a/c/cert/src/rules/CON35-C/DeadlockByLockingInPredefinedOrder.ql
+++ b/c/cert/src/rules/CON35-C/DeadlockByLockingInPredefinedOrder.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con35-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -16,7 +21,8 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.rules.preventdeadlockbylockinginpredefinedorder.PreventDeadlockByLockingInPredefinedOrder
-class DeadlockByLockingInPredefinedOrderQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery {
+class DeadlockByLockingInPredefinedOrderQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery
+{
DeadlockByLockingInPredefinedOrderQuery() {
this = Concurrency2Package::deadlockByLockingInPredefinedOrderQuery()
}
diff --git a/c/cert/src/rules/CON36-C/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql b/c/cert/src/rules/CON36-C/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql
index 430a0e7c19..d0d948d9b2 100644
--- a/c/cert/src/rules/CON36-C/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql
+++ b/c/cert/src/rules/CON36-C/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con36-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/CON37-C/DoNotCallSignalInMultithreadedProgram.ql b/c/cert/src/rules/CON37-C/DoNotCallSignalInMultithreadedProgram.ql
index 68e0c97ea9..17691f24dd 100644
--- a/c/cert/src/rules/CON37-C/DoNotCallSignalInMultithreadedProgram.ql
+++ b/c/cert/src/rules/CON37-C/DoNotCallSignalInMultithreadedProgram.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con37-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
@@ -24,5 +29,4 @@ where
not isExcluded(fc, Concurrency1Package::doNotCallSignalInMultithreadedProgramQuery()) and
fc.getTarget().getName() = "signal" and
exists(ThreadedFunction f)
-select fc,
- "Call to `signal()` in multithreaded programs."
+select fc, "Call to `signal()` in multithreaded programs."
diff --git a/c/cert/src/rules/CON38-C/PreserveSafetyWhenUsingConditionVariables.ql b/c/cert/src/rules/CON38-C/PreserveSafetyWhenUsingConditionVariables.ql
index dad45dd592..3b2ae558d8 100644
--- a/c/cert/src/rules/CON38-C/PreserveSafetyWhenUsingConditionVariables.ql
+++ b/c/cert/src/rules/CON38-C/PreserveSafetyWhenUsingConditionVariables.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/con38-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -16,7 +21,8 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.rules.preservesafetywhenusingconditionvariables.PreserveSafetyWhenUsingConditionVariables
-class PreserveSafetyWhenUsingConditionVariablesQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery {
+class PreserveSafetyWhenUsingConditionVariablesQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery
+{
PreserveSafetyWhenUsingConditionVariablesQuery() {
this = Concurrency3Package::preserveSafetyWhenUsingConditionVariablesQuery()
}
diff --git a/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql
index a8dead535d..6ef617ca72 100644
--- a/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql
+++ b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql
@@ -9,42 +9,20 @@
* @tags external/cert/id/con39-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
-import codingstandards.cpp.Concurrency
+import codingstandards.cpp.rules.joinordetachthreadonlyonce.JoinOrDetachThreadOnlyOnce
-// OK
-// 1) Thread calls detach parent DOES NOT call join
-// 2) Parent calls join, thread does NOT call detach()
-// NOT OK
-// 1) Thread calls detach, parent calls join
-// 2) Thread calls detach twice, parent does not call join
-// 3) Parent calls join twice, thread does not call detach
-from C11ThreadCreateCall tcc
-where
- not isExcluded(tcc, Concurrency5Package::threadWasPreviouslyJoinedOrDetachedQuery()) and
- // Note: These cases can be simplified but they are presented like this for clarity
- // case 1 - calls to `thrd_join` and `thrd_detach` within the parent or
- // within the parent / child CFG.
- exists(C11ThreadWait tw, C11ThreadDetach dt |
- tw = getAThreadContextAwareSuccessor(tcc) and
- dt = getAThreadContextAwareSuccessor(tcc)
- )
- or
- // case 2 - multiple calls to `thrd_detach` within the threaded CFG.
- exists(C11ThreadDetach dt1, C11ThreadDetach dt2 |
- dt1 = getAThreadContextAwareSuccessor(tcc) and
- dt2 = getAThreadContextAwareSuccessor(tcc) and
- not dt1 = dt2
- )
- or
- // case 3 - multiple calls to `thrd_join` within the threaded CFG.
- exists(C11ThreadWait tw1, C11ThreadWait tw2 |
- tw1 = getAThreadContextAwareSuccessor(tcc) and
- tw2 = getAThreadContextAwareSuccessor(tcc) and
- not tw1 = tw2
- )
-select tcc, "Thread may call join or detach after the thread is joined or detached."
+class ThreadWasPreviouslyJoinedOrDetachedQuery extends JoinOrDetachThreadOnlyOnceSharedQuery {
+ ThreadWasPreviouslyJoinedOrDetachedQuery() {
+ this = Concurrency5Package::threadWasPreviouslyJoinedOrDetachedQuery()
+ }
+}
diff --git a/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql
index d20663bd87..0ec195868f 100644
--- a/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql
+++ b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql
@@ -9,11 +9,17 @@
* @tags external/cert/id/con40-c
* correctness
* concurrency
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
+import codingstandards.cpp.Concurrency
from MacroInvocation mi, Variable v, Locatable whereFound
where
@@ -22,13 +28,13 @@ where
// There isn't a way to safely use this construct in a way that is also
// possible the reliably detect so advise against using it.
(
- mi.getMacroName() = ["atomic_store", "atomic_store_explicit"]
+ mi instanceof AtomicStore
or
// This construct is generally safe, but must be used in a loop. To lower
// the false positive rate we don't look at the conditions of the loop and
// instead assume if it is found in a looping construct that it is likely
// related to the safety property.
- mi.getMacroName() = ["atomic_compare_exchange_weak", "atomic_compare_exchange_weak_explicit"] and
+ mi instanceof AtomicCompareExchange and
not exists(Loop l | mi.getAGeneratedElement().(Expr).getParent*() = l)
) and
whereFound = mi
diff --git a/c/cert/src/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.ql b/c/cert/src/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.ql
index 62c8ec5dc4..57be1bc488 100644
--- a/c/cert/src/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.ql
+++ b/c/cert/src/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.ql
@@ -9,31 +9,24 @@
* @tags external/cert/id/con41-c
* correctness
* concurrency
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
+import codingstandards.cpp.Concurrency
-/**
- * Models calls to routines in the `stdatomic` library. Note that these
- * are typically implemented as macros within Clang and GCC's standard
- * libraries.
- */
-class SpuriouslyFailingFunctionCallType extends MacroInvocation {
- SpuriouslyFailingFunctionCallType() {
- getMacroName() = ["atomic_compare_exchange_weak", "atomic_compare_exchange_weak_explicit"]
- }
-}
-
-from SpuriouslyFailingFunctionCallType fc
+from AtomicCompareExchange ace
where
- not isExcluded(fc, Concurrency3Package::wrapFunctionsThatCanFailSpuriouslyInLoopQuery()) and
+ not isExcluded(ace, Concurrency3Package::wrapFunctionsThatCanFailSpuriouslyInLoopQuery()) and
(
- exists(StmtParent sp | sp = fc.getStmt() and not sp.(Stmt).getParentStmt*() instanceof Loop)
+ forex(StmtParent sp | sp = ace.getStmt() | not sp.(Stmt).getParentStmt*() instanceof Loop)
or
- exists(StmtParent sp |
- sp = fc.getExpr() and not sp.(Expr).getEnclosingStmt().getParentStmt*() instanceof Loop
- )
+ forex(Expr e | e = ace.getExpr() | not e.getEnclosingStmt().getParentStmt*() instanceof Loop)
)
-select fc, "Function that can spuriously fail not wrapped in a loop."
+select ace, "Function that can spuriously fail not wrapped in a loop."
diff --git a/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.md b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.md
new file mode 100644
index 0000000000..8124cd49cd
--- /dev/null
+++ b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.md
@@ -0,0 +1,190 @@
+# DCL30-C: Declare objects with appropriate storage durations
+
+This query implements the CERT-C rule DCL30-C:
+
+> Declare objects with appropriate storage durations
+
+
+## Description
+
+Every object has a storage duration that determines its lifetime: *static*, *thread*, *automatic*, or *allocated*.
+
+According to the C Standard, 6.2.4, paragraph 2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\],
+
+> The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
+
+
+Do not attempt to access an object outside of its lifetime. Attempting to do so is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) and can lead to an exploitable [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability). (See also [undefined behavior 9](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_9) in the C Standard, Annex J.)
+
+## Noncompliant Code Example (Differing Storage Durations)
+
+In this noncompliant code example, the address of the variable `c_str` with automatic storage duration is assigned to the variable `p`, which has static storage duration. The assignment itself is valid, but it is invalid for `c_str` to go out of scope while `p` holds its address, as happens at the end of `dont_do_this``()`.
+
+```cpp
+#include
+
+const char *p;
+void dont_do_this(void) {
+ const char c_str[] = "This will change";
+ p = c_str; /* Dangerous */
+}
+
+void innocuous(void) {
+ printf("%s\n", p);
+}
+
+int main(void) {
+ dont_do_this();
+ innocuous();
+ return 0;
+}
+```
+
+## Compliant Solution (Same Storage Durations)
+
+In this compliant solution, `p` is declared with the same storage duration as `c_str`, preventing `p` from taking on an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) outside of `this_is_OK()`:
+
+```cpp
+void this_is_OK(void) {
+ const char c_str[] = "Everything OK";
+ const char *p = c_str;
+ /* ... */
+}
+/* p is inaccessible outside the scope of string c_str */
+
+```
+Alternatively, both `p` and `c_str` could be declared with static storage duration.
+
+## Compliant Solution (Differing Storage Durations)
+
+If it is necessary for `p` to be defined with static storage duration but `c_str` with a more limited duration, then `p` can be set to `NULL` before `c_str` is destroyed. This practice prevents `p` from taking on an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue), although any references to `p` must check for `NULL`.
+
+```cpp
+const char *p;
+void is_this_OK(void) {
+ const char c_str[] = "Everything OK?";
+ p = c_str;
+ /* ... */
+ p = NULL;
+}
+
+```
+
+## Noncompliant Code Example (Return Values)
+
+In this noncompliant code sample, the function `init_array``()` returns a pointer to a character array with automatic storage duration, which is accessible to the caller:
+
+```cpp
+char *init_array(void) {
+ char array[10];
+ /* Initialize array */
+ return array;
+}
+
+```
+Some compilers generate a diagnostic message when a pointer to an object with automatic storage duration is returned from a function, as in this example. Programmers should compile code at high warning levels and resolve any diagnostic messages. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).)
+
+## Compliant Solution (Return Values)
+
+The solution, in this case, depends on the intent of the programmer. If the intent is to modify the value of `array` and have that modification persist outside the scope of `init_array()`, the desired behavior can be achieved by declaring `array` elsewhere and passing it as an argument to `init_array()`:
+
+```cpp
+#include
+void init_array(char *array, size_t len) {
+ /* Initialize array */
+ return;
+}
+
+int main(void) {
+ char array[10];
+ init_array(array, sizeof(array) / sizeof(array[0]));
+ /* ... */
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example (Output Parameter)
+
+In this noncompliant code example, the function `squirrel_away()` stores a pointer to local variable `local` into a location pointed to by function parameter `ptr_param`. Upon the return of `squirrel_away()`, the pointer `ptr_param` points to a variable that has an expired lifetime.
+
+```cpp
+void squirrel_away(char **ptr_param) {
+ char local[10];
+ /* Initialize array */
+ *ptr_param = local;
+}
+
+void rodent(void) {
+ char *ptr;
+ squirrel_away(&ptr);
+ /* ptr is live but invalid here */
+}
+
+```
+
+## Compliant Solution (Output Parameter)
+
+In this compliant solution, the variable `local` has static storage duration; consequently, `ptr` can be used to reference the `local` array within the `rodent()` function:
+
+```cpp
+char local[10];
+
+void squirrel_away(char **ptr_param) {
+ /* Initialize array */
+ *ptr_param = local;
+}
+
+void rodent(void) {
+ char *ptr;
+ squirrel_away(&ptr);
+ /* ptr is valid in this scope */
+}
+
+```
+
+## Risk Assessment
+
+Referencing an object outside of its lifetime can result in an attacker being able to execute arbitrary code.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
DCL30-C | High | Probable | High | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointered-deallocation return-reference-local | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-DCL30 | Fully implemented |
CodeSonar | 7.2p0 | LANG.STRUCT.RPL | Returns pointer to local |
Compass/ROSE | | | Can detect violations of this rule. It automatically detects returning pointers to local variables. Detecting more general cases, such as examples where static pointers are set to local variables which then go out of scope, would be difficult |
Coverity | 2017.07 | RETURN_LOCAL | Finds many instances where a function will return a pointer to a local stack variable. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary |
Helix QAC | 2022.4 | C3217, C3225, C3230, C4140 C++2515, C++2516, C++2527, C++2528, C++4026, C++4624, C++4629 | |
Klocwork | 2022.4 | LOCRET.ARGLOCRET.GLOB LOCRET.RET | |
LDRA tool suite | 9.7.1 | 42 D, 77 D, 71 S, 565 S | Enhanced Enforcement |
Parasoft C/C++test | 2022.2 | CERT_C-DCL30-a CERT_C-DCL30-b | The address of an object with automatic storage shall not be returned from a function The address of an object with automatic storage shall not be assigned to another object that may persist after the first object has ceased to exist |
PC-lint Plus | 1.4 | 604, 674, 733, 789 | Partially supported |
Polyspace Bug Finder | R2022b | CERT C: Rule DCL30-C | Checks for pointer or reference to stack variable leaving scope (rule fully covered) |
PRQA QA-C | 9.7 | 3217, 3225, 3230, 4140 | Partially implemented |
PRQA QA-C++ | 4.4 | 2515, 2516, 2527, 2528, 4026, 4624, 4629 | |
PVS-Studio | 7.23 | V506 , V507 , V558 , V623 , V723 , V738 | |
RuleChecker | 22.04 | return-reference-local | Partially checked |
Splint | 3.1.1 | | |
TrustInSoft Analyzer | 1.38 | dangling_pointer | Exhaustively detects undefined behavior (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+DCL30-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-562 and DCL30-C**
+
+DCL30-C = Union( CWE-562, list) where list =
+
+* Assigning a stack pointer to an argument (thereby letting it outlive the current function
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+The rule checks specifically for pointers to objects with automatic storage duration that are returned by functions or assigned to function output parameters.
+
+## References
+
+* CERT-C: [DCL30-C: Declare objects with appropriate storage durations](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.ql b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.ql
new file mode 100644
index 0000000000..2e1064ee9d
--- /dev/null
+++ b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.ql
@@ -0,0 +1,58 @@
+/**
+ * @id c/cert/appropriate-storage-durations-function-return
+ * @name DCL30-C: Declare objects with appropriate storage durations
+ * @description When pointers to local variables are returned by a function it can lead to referring
+ * to objects outside of their lifetime, which is undefined behaviour.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/dcl30-c
+ * correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.c.Objects
+import semmle.code.cpp.dataflow.DataFlow
+
+class Source extends Expr {
+ ObjectIdentity rootObject;
+
+ Source() {
+ rootObject.getStorageDuration().isAutomatic() and
+ this = rootObject.getASubobjectAddressExpr()
+ }
+}
+
+class Sink extends DataFlow::Node {
+ Sink() {
+ //output parameter
+ exists(Parameter f |
+ f.getAnAccess() = this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() and
+ f.getUnderlyingType() instanceof PointerType
+ )
+ or
+ //function returns pointer
+ exists(Function f, ReturnStmt r |
+ f.getType() instanceof PointerType and
+ r.getEnclosingFunction() = f and
+ r.getExpr() = this.asExpr()
+ )
+ }
+}
+
+from DataFlow::Node src, DataFlow::Node sink
+where
+ not isExcluded(sink.asExpr(),
+ Declarations8Package::appropriateStorageDurationsFunctionReturnQuery()) and
+ exists(Source s | src.asExpr() = s) and
+ sink instanceof Sink and
+ DataFlow::localFlow(src, sink)
+select sink, "$@ with automatic storage may be accessible outside of its lifetime.", src,
+ src.toString()
diff --git a/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.md b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.md
new file mode 100644
index 0000000000..1926ffd7aa
--- /dev/null
+++ b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.md
@@ -0,0 +1,190 @@
+# DCL30-C: Declare objects with appropriate storage durations
+
+This query implements the CERT-C rule DCL30-C:
+
+> Declare objects with appropriate storage durations
+
+
+## Description
+
+Every object has a storage duration that determines its lifetime: *static*, *thread*, *automatic*, or *allocated*.
+
+According to the C Standard, 6.2.4, paragraph 2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\],
+
+> The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
+
+
+Do not attempt to access an object outside of its lifetime. Attempting to do so is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) and can lead to an exploitable [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability). (See also [undefined behavior 9](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_9) in the C Standard, Annex J.)
+
+## Noncompliant Code Example (Differing Storage Durations)
+
+In this noncompliant code example, the address of the variable `c_str` with automatic storage duration is assigned to the variable `p`, which has static storage duration. The assignment itself is valid, but it is invalid for `c_str` to go out of scope while `p` holds its address, as happens at the end of `dont_do_this``()`.
+
+```cpp
+#include
+
+const char *p;
+void dont_do_this(void) {
+ const char c_str[] = "This will change";
+ p = c_str; /* Dangerous */
+}
+
+void innocuous(void) {
+ printf("%s\n", p);
+}
+
+int main(void) {
+ dont_do_this();
+ innocuous();
+ return 0;
+}
+```
+
+## Compliant Solution (Same Storage Durations)
+
+In this compliant solution, `p` is declared with the same storage duration as `c_str`, preventing `p` from taking on an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) outside of `this_is_OK()`:
+
+```cpp
+void this_is_OK(void) {
+ const char c_str[] = "Everything OK";
+ const char *p = c_str;
+ /* ... */
+}
+/* p is inaccessible outside the scope of string c_str */
+
+```
+Alternatively, both `p` and `c_str` could be declared with static storage duration.
+
+## Compliant Solution (Differing Storage Durations)
+
+If it is necessary for `p` to be defined with static storage duration but `c_str` with a more limited duration, then `p` can be set to `NULL` before `c_str` is destroyed. This practice prevents `p` from taking on an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue), although any references to `p` must check for `NULL`.
+
+```cpp
+const char *p;
+void is_this_OK(void) {
+ const char c_str[] = "Everything OK?";
+ p = c_str;
+ /* ... */
+ p = NULL;
+}
+
+```
+
+## Noncompliant Code Example (Return Values)
+
+In this noncompliant code sample, the function `init_array``()` returns a pointer to a character array with automatic storage duration, which is accessible to the caller:
+
+```cpp
+char *init_array(void) {
+ char array[10];
+ /* Initialize array */
+ return array;
+}
+
+```
+Some compilers generate a diagnostic message when a pointer to an object with automatic storage duration is returned from a function, as in this example. Programmers should compile code at high warning levels and resolve any diagnostic messages. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).)
+
+## Compliant Solution (Return Values)
+
+The solution, in this case, depends on the intent of the programmer. If the intent is to modify the value of `array` and have that modification persist outside the scope of `init_array()`, the desired behavior can be achieved by declaring `array` elsewhere and passing it as an argument to `init_array()`:
+
+```cpp
+#include
+void init_array(char *array, size_t len) {
+ /* Initialize array */
+ return;
+}
+
+int main(void) {
+ char array[10];
+ init_array(array, sizeof(array) / sizeof(array[0]));
+ /* ... */
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example (Output Parameter)
+
+In this noncompliant code example, the function `squirrel_away()` stores a pointer to local variable `local` into a location pointed to by function parameter `ptr_param`. Upon the return of `squirrel_away()`, the pointer `ptr_param` points to a variable that has an expired lifetime.
+
+```cpp
+void squirrel_away(char **ptr_param) {
+ char local[10];
+ /* Initialize array */
+ *ptr_param = local;
+}
+
+void rodent(void) {
+ char *ptr;
+ squirrel_away(&ptr);
+ /* ptr is live but invalid here */
+}
+
+```
+
+## Compliant Solution (Output Parameter)
+
+In this compliant solution, the variable `local` has static storage duration; consequently, `ptr` can be used to reference the `local` array within the `rodent()` function:
+
+```cpp
+char local[10];
+
+void squirrel_away(char **ptr_param) {
+ /* Initialize array */
+ *ptr_param = local;
+}
+
+void rodent(void) {
+ char *ptr;
+ squirrel_away(&ptr);
+ /* ptr is valid in this scope */
+}
+
+```
+
+## Risk Assessment
+
+Referencing an object outside of its lifetime can result in an attacker being able to execute arbitrary code.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
DCL30-C | High | Probable | High | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointered-deallocation return-reference-local | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-DCL30 | Fully implemented |
CodeSonar | 7.2p0 | LANG.STRUCT.RPL | Returns pointer to local |
Compass/ROSE | | | Can detect violations of this rule. It automatically detects returning pointers to local variables. Detecting more general cases, such as examples where static pointers are set to local variables which then go out of scope, would be difficult |
Coverity | 2017.07 | RETURN_LOCAL | Finds many instances where a function will return a pointer to a local stack variable. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary |
Helix QAC | 2022.4 | C3217, C3225, C3230, C4140 C++2515, C++2516, C++2527, C++2528, C++4026, C++4624, C++4629 | |
Klocwork | 2022.4 | LOCRET.ARGLOCRET.GLOB LOCRET.RET | |
LDRA tool suite | 9.7.1 | 42 D, 77 D, 71 S, 565 S | Enhanced Enforcement |
Parasoft C/C++test | 2022.2 | CERT_C-DCL30-a CERT_C-DCL30-b | The address of an object with automatic storage shall not be returned from a function The address of an object with automatic storage shall not be assigned to another object that may persist after the first object has ceased to exist |
PC-lint Plus | 1.4 | 604, 674, 733, 789 | Partially supported |
Polyspace Bug Finder | R2022b | CERT C: Rule DCL30-C | Checks for pointer or reference to stack variable leaving scope (rule fully covered) |
PRQA QA-C | 9.7 | 3217, 3225, 3230, 4140 | Partially implemented |
PRQA QA-C++ | 4.4 | 2515, 2516, 2527, 2528, 4026, 4624, 4629 | |
PVS-Studio | 7.23 | V506 , V507 , V558 , V623 , V723 , V738 | |
RuleChecker | 22.04 | return-reference-local | Partially checked |
Splint | 3.1.1 | | |
TrustInSoft Analyzer | 1.38 | dangling_pointer | Exhaustively detects undefined behavior (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+DCL30-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-562 and DCL30-C**
+
+DCL30-C = Union( CWE-562, list) where list =
+
+* Assigning a stack pointer to an argument (thereby letting it outlive the current function
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+The rule checks specifically for pointers to objects with automatic storage duration that are assigned to static storage duration variables.
+
+## References
+
+* CERT-C: [DCL30-C: Declare objects with appropriate storage durations](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.ql b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.ql
new file mode 100644
index 0000000000..a5749aa8bc
--- /dev/null
+++ b/c/cert/src/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.ql
@@ -0,0 +1,28 @@
+/**
+ * @id c/cert/appropriate-storage-durations-stack-adress-escape
+ * @name DCL30-C: Declare objects with appropriate storage durations
+ * @description When storage durations are not compatible between assigned pointers it can lead to
+ * referring to objects outside of their lifetime, which is undefined behaviour.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/dcl30-c
+ * correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.donotcopyaddressofautostorageobjecttootherobject.DoNotCopyAddressOfAutoStorageObjectToOtherObject
+
+class AppropriateStorageDurationsStackAdressEscapeQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery
+{
+ AppropriateStorageDurationsStackAdressEscapeQuery() {
+ this = Declarations8Package::appropriateStorageDurationsStackAdressEscapeQuery()
+ }
+}
diff --git a/c/cert/src/rules/DCL31-C/DeclareIdentifiersBeforeUsingThem.ql b/c/cert/src/rules/DCL31-C/DeclareIdentifiersBeforeUsingThem.ql
index 369baa4a63..35e6cd057a 100644
--- a/c/cert/src/rules/DCL31-C/DeclareIdentifiersBeforeUsingThem.ql
+++ b/c/cert/src/rules/DCL31-C/DeclareIdentifiersBeforeUsingThem.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/dcl31-c
* correctness
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/DCL37-C/DoNotDeclareOrDefineAReservedIdentifier.ql b/c/cert/src/rules/DCL37-C/DoNotDeclareOrDefineAReservedIdentifier.ql
index 99c5a9708b..04a3030cc1 100644
--- a/c/cert/src/rules/DCL37-C/DoNotDeclareOrDefineAReservedIdentifier.ql
+++ b/c/cert/src/rules/DCL37-C/DoNotDeclareOrDefineAReservedIdentifier.ql
@@ -9,6 +9,11 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql b/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql
index e9fa3f1017..d6000852c6 100644
--- a/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql
+++ b/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql
@@ -10,6 +10,11 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.md b/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.md
new file mode 100644
index 0000000000..4dd3bcbe3c
--- /dev/null
+++ b/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.md
@@ -0,0 +1,292 @@
+# DCL39-C: Avoid information leakage when passing a structure across a trust boundary
+
+This query implements the CERT-C rule DCL39-C:
+
+> Avoid information leakage when passing a structure across a trust boundary
+
+
+## Description
+
+The C Standard, 6.7.2.1, discusses the layout of structure fields. It specifies that non-bit-field members are aligned in an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) manner and that there may be padding within or at the end of a structure. Furthermore, initializing the members of the structure does not guarantee initialization of the padding bytes. The C Standard, 6.2.6.1, paragraph 6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.
+
+
+Additionally, the storage units in which a bit-field resides may also have padding bits. For an object with automatic storage duration, these padding bits do not take on specific values and can contribute to leaking sensitive information.
+
+When passing a pointer to a structure across a trust boundary to a different trusted domain, the programmer must ensure that the padding bytes and bit-field storage unit padding bits of such a structure do not contain sensitive information.
+
+## Noncompliant Code Example
+
+This noncompliant code example runs in kernel space and copies data from `arg` to user space. However, padding bytes may be used within the structure, for example, to ensure the proper alignment of the structure members. These padding bytes may contain sensitive information, which may then be leaked when the data is copied to user space.
+
+```cpp
+#include
+
+struct test {
+ int a;
+ char b;
+ int c;
+};
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = {.a = 1, .b = 2, .c = 3};
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+
+```
+
+## Noncompliant Code Example (memset())
+
+The padding bytes can be explicitly initialized by calling `memset()`:
+
+```cpp
+#include
+
+struct test {
+ int a;
+ char b;
+ int c;
+};
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg;
+
+ /* Set all bytes (including padding bytes) to zero */
+ memset(&arg, 0, sizeof(arg));
+
+ arg.a = 1;
+ arg.b = 2;
+ arg.c = 3;
+
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+
+```
+However, a conforming compiler is free to implement `arg.b = 2` by setting the low-order bits of a register to 2, leaving the high-order bits unchanged and containing sensitive information. Then the platform copies all register bits into memory, leaving sensitive information in the padding bits. Consequently, this implementation could leak the high-order bits from the register to a user.
+
+## Compliant Solution
+
+This compliant solution serializes the structure data before copying it to an untrusted context:
+
+```cpp
+#include
+#include
+
+struct test {
+ int a;
+ char b;
+ int c;
+};
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = {.a = 1, .b = 2, .c = 3};
+ /* May be larger than strictly needed */
+ unsigned char buf[sizeof(arg)];
+ size_t offset = 0;
+
+ memcpy(buf + offset, &arg.a, sizeof(arg.a));
+ offset += sizeof(arg.a);
+ memcpy(buf + offset, &arg.b, sizeof(arg.b));
+ offset += sizeof(arg.b);
+ memcpy(buf + offset, &arg.c, sizeof(arg.c));
+ offset += sizeof(arg.c);
+ /* Set all remaining bytes to zero */
+ memset(buf + offset, 0, sizeof(arg) - offset);
+
+ copy_to_user(usr_buf, buf, offset /* size of info copied */);
+}
+```
+This code ensures that no uninitialized padding bytes are copied to unprivileged users. **Important:** The structure copied to user space is now a packed structure and the `copy_to_user()` function (or other eventual user) would need to unpack it to recreate the original padded structure.
+
+## Compliant Solution (Padding Bytes)
+
+Padding bytes can be explicitly declared as fields within the structure. This solution is not portable, however, because it depends on the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) and target memory architecture. The following solution is specific to the x86-32 architecture:
+
+```cpp
+#include
+#include
+
+struct test {
+ int a;
+ char b;
+ char padding_1, padding_2, padding_3;
+ int c;
+};
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ /* Ensure c is the next byte after the last padding byte */
+ static_assert(offsetof(struct test, c) ==
+ offsetof(struct test, padding_3) + 1,
+ "Structure contains intermediate padding");
+ /* Ensure there is no trailing padding */
+ static_assert(sizeof(struct test) ==
+ offsetof(struct test, c) + sizeof(int),
+ "Structure contains trailing padding");
+ struct test arg = {.a = 1, .b = 2, .c = 3};
+ arg.padding_1 = 0;
+ arg.padding_2 = 0;
+ arg.padding_3 = 0;
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+
+```
+The C Standard `static_assert()` macro accepts a constant expression and an [error message](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-error). The expression is evaluated at compile time and, if false, the compilation is terminated and the error message is output. (See [DCL03-C. Use a static assertion to test the value of a constant expression](https://wiki.sei.cmu.edu/confluence/display/c/DCL03-C.+Use+a+static+assertion+to+test+the+value+of+a+constant+expression) for more details.) The explicit insertion of the padding bytes into the `struct` should ensure that no additional padding bytes are added by the compiler and consequently both static assertions should be true. However, it is necessary to validate these assumptions to ensure that the solution is correct for a particular implementation.
+
+## Compliant Solution (Structure Packing—GCC)
+
+GCC allows specifying declaration attributes using the keyword `__attribute__((__packed__))`. When this attribute is present, the compiler will not add padding bytes for memory alignment unless an explicit alignment specifier for a structure member requires the introduction of padding bytes.
+
+```cpp
+#include
+
+struct test {
+ int a;
+ char b;
+ int c;
+} __attribute__((__packed__));
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = {.a = 1, .b = 2, .c = 3};
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+
+```
+
+## Compliant Solution (Structure Packing—Microsoft Visual Studio)
+
+Microsoft Visual Studio supports `#pragma pack()` to suppress padding bytes \[[MSDN](http://msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.110).aspx)\]. The compiler adds padding bytes for memory alignment, depending on the current packing mode, but still honors the alignment specified by `__declspec(align())`. In this compliant solution, the packing mode is set to 1 in an attempt to ensure all fields are given adjacent offsets:
+
+```cpp
+#include
+
+#pragma pack(push, 1) /* 1 byte */
+struct test {
+ int a;
+ char b;
+ int c;
+};
+#pragma pack(pop)
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = {1, 2, 3};
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+
+```
+The `pack` pragma takes effect at the first `struct` declaration after the pragma is seen.
+
+## Noncompliant Code Example
+
+This noncompliant code example also runs in kernel space and copies data from `struct test` to user space. However, padding bits will be used within the structure due to the bit-field member lengths not adding up to the number of bits in an `unsigned` object. Further, there is an unnamed bit-field that causes no further bit-fields to be packed into the same storage unit. These padding bits may contain sensitive information, which may then be leaked when the data is copied to user space. For instance, the uninitialized bits may contain a sensitive kernel space pointer value that can be trivially reconstructed by an attacker in user space.
+
+```cpp
+#include
+
+struct test {
+ unsigned a : 1;
+ unsigned : 0;
+ unsigned b : 4;
+};
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = { .a = 1, .b = 10 };
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+```
+
+## Compliant Solution
+
+Padding bits can be explicitly declared, allowing the programmer to specify the value of those bits. When explicitly declaring all of the padding bits, any unnamed bit-fields of length `0` must be removed from the structure because the explicit padding bits ensure that no further bit-fields will be packed into the same storage unit.
+
+```cpp
+#include
+#include
+#include
+
+struct test {
+ unsigned a : 1;
+ unsigned padding1 : sizeof(unsigned) * CHAR_BIT - 1;
+ unsigned b : 4;
+ unsigned padding2 : sizeof(unsigned) * CHAR_BIT - 4;
+};
+/* Ensure that we have added the correct number of padding bits. */
+static_assert(sizeof(struct test) == sizeof(unsigned) * 2,
+ "Incorrect number of padding bits for type: unsigned");
+
+/* Safely copy bytes to user space */
+extern int copy_to_user(void *dest, void *src, size_t size);
+
+void do_stuff(void *usr_buf) {
+ struct test arg = { .a = 1, .padding1 = 0, .b = 10, .padding2 = 0 };
+ copy_to_user(usr_buf, &arg, sizeof(arg));
+}
+```
+This solution is not portable, however, because it depends on the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) and target memory architecture. The explicit insertion of padding bits into the `struct` should ensure that no additional padding bits are added by the compiler. However, it is still necessary to validate these assumptions to ensure that the solution is correct for a particular implementation. For instance, the DEC Alpha is an example of a 64-bit architecture with 32-bit integers that allocates 64 bits to a storage unit.
+
+In addition, this solution assumes that there are no integer padding bits in an `unsigned int`. The portable version of the width calculation from [INT35-C. Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions) cannot be used because the bit-field width must be an integer constant expression.
+
+From this situation, it can be seen that special care must be taken because no solution to the bit-field padding issue will be 100% portable.
+
+## Risk Assessment
+
+Padding units might contain sensitive data because the C Standard allows any padding to take [unspecified values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unspecifiedvalue). A pointer to such a structure could be passed to other functions, causing information leakage.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
DCL39-C | Low | Unlikely | High | P1 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | function-argument-with-padding | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-DCL39 | Detects composite structures with padding, in particular those passed to trust boundary routines. |
CodeSonar | 7.2p0 | MISC.PADDING.POTB | Padding Passed Across a Trust Boundary |
Helix QAC | 2022.4 | DF4941, DF4942, DF4943 | |
Klocwork | 2022.4 | PORTING.STORAGE.STRUCT | |
Parasoft C/C++test | 2022.2 | CERT_C-DCL39-a | A pointer to a structure should not be passed to a function that can copy data to the user space |
Polyspace Bug Finder | R2022b | CERT C: Rule DCL39-C | Checks for information leak via structure padding |
PRQA QA-C | 9.7 | 4941, 4942, 4943 | |
PRQA QA-C++ | 4.4 | 4941, 4942, 4943 | |
RuleChecker | 22.04 | function-argument-with-padding | Partially checked |
+
+
+## Related Vulnerabilities
+
+Numerous vulnerabilities in the Linux Kernel have resulted from violations of this rule. [CVE-2010-4083](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-4083) describes a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in which the `semctl()` system call allows unprivileged users to read uninitialized kernel stack memory because various fields of a `semid_ds struct` declared on the stack are not altered or zeroed before being copied back to the user.
+
+[CVE-2010-3881](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3881) describes a vulnerability in which structure padding and reserved fields in certain data structures in `QEMU-KVM` were not initialized properly before being copied to user space. A privileged host user with access to `/dev/kvm` could use this [flaw](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-securityflaw) to leak kernel stack memory to user space.
+
+[CVE-2010-3477](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3477) describes a kernel information leak in `act_police` where incorrectly initialized structures in the traffic-control dump code may allow the disclosure of kernel memory to user space applications.
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+DCL39-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+The rule does not detect cases where fields may have uninitialized padding but are initialized via an initializer.
+
+## References
+
+* CERT-C: [DCL39-C: Avoid information leakage when passing a structure across a trust boundary](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.ql b/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.ql
new file mode 100644
index 0000000000..dd2c1217cf
--- /dev/null
+++ b/c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.ql
@@ -0,0 +1,28 @@
+/**
+ * @id c/cert/information-leakage-across-trust-boundaries-c
+ * @name DCL39-C: Avoid information leakage when passing a structure across a trust boundary
+ * @description Passing a structure with uninitialized fields or padding bytes can cause information
+ * to be unintentionally leaked.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/dcl39-c
+ * security
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p1
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.informationleakageacrossboundaries.InformationLeakageAcrossBoundaries
+
+class InformationLeakageAcrossTrustBoundariesCQuery extends InformationLeakageAcrossBoundariesSharedQuery
+{
+ InformationLeakageAcrossTrustBoundariesCQuery() {
+ this = Declarations7Package::informationLeakageAcrossTrustBoundariesCQuery()
+ }
+}
diff --git a/c/cert/src/rules/DCL40-C/ExcessLengthNamesIdentifiersNotDistinct.ql b/c/cert/src/rules/DCL40-C/ExcessLengthNamesIdentifiersNotDistinct.ql
index ba2cc5c23f..d002326fae 100644
--- a/c/cert/src/rules/DCL40-C/ExcessLengthNamesIdentifiersNotDistinct.ql
+++ b/c/cert/src/rules/DCL40-C/ExcessLengthNamesIdentifiersNotDistinct.ql
@@ -9,6 +9,11 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql b/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql
index 4660e69b68..3811d4e417 100644
--- a/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql
+++ b/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql
@@ -11,35 +11,52 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
+import codingstandards.cpp.types.Compatible
import ExternalIdentifiers
-//checks if they are incompatible based on return type, number of parameters and parameter types
-predicate checkMatchingFunction(FunctionDeclarationEntry d, FunctionDeclarationEntry d2) {
- not d.getType() = d2.getType()
- or
- not d.getNumberOfParameters() = d2.getNumberOfParameters()
- or
- exists(ParameterDeclarationEntry p, ParameterDeclarationEntry p2, int i |
- d.getParameterDeclarationEntry(i) = p and
- d2.getParameterDeclarationEntry(i) = p2 and
- not p.getType() = p2.getType()
- )
+predicate interestedInFunctions(
+ FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, ExternalIdentifiers d
+) {
+ not f1 = f2 and
+ d = f1.getDeclaration() and
+ d = f2.getDeclaration()
+}
+
+predicate interestedInFunctions(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) {
+ interestedInFunctions(f1, f2, _)
}
+module FuncDeclEquiv =
+ FunctionDeclarationTypeEquivalence;
+
from ExternalIdentifiers d, FunctionDeclarationEntry f1, FunctionDeclarationEntry f2
where
not isExcluded(f1, Declarations2Package::incompatibleFunctionDeclarationsQuery()) and
not isExcluded(f2, Declarations2Package::incompatibleFunctionDeclarationsQuery()) and
- f1 = d.getADeclarationEntry() and
- f2 = d.getADeclarationEntry() and
- not f1 = f2 and
- f1.getLocation().getStartLine() >= f2.getLocation().getStartLine() and
- f1.getName() = f2.getName() and
- checkMatchingFunction(f1, f2)
+ interestedInFunctions(f1, f2, d) and
+ (
+ //return type check
+ not FuncDeclEquiv::equalReturnTypes(f1, f2)
+ or
+ //parameter type check
+ not FuncDeclEquiv::equalParameterTypes(f1, f2)
+ ) and
+ // Apply ordering on start line, trying to avoid the optimiser applying this join too early
+ // in the pipeline
+ exists(int f1Line, int f2Line |
+ f1.getLocation().hasLocationInfo(_, f1Line, _, _, _) and
+ f2.getLocation().hasLocationInfo(_, f2Line, _, _, _) and
+ f1Line >= f2Line
+ )
select f1, "The object $@ is not compatible with re-declaration $@", f1, f1.getName(), f2,
f2.getName()
diff --git a/c/cert/src/rules/DCL40-C/IncompatibleObjectDeclarations.ql b/c/cert/src/rules/DCL40-C/IncompatibleObjectDeclarations.ql
index 151d33db5c..8e220062d4 100644
--- a/c/cert/src/rules/DCL40-C/IncompatibleObjectDeclarations.ql
+++ b/c/cert/src/rules/DCL40-C/IncompatibleObjectDeclarations.ql
@@ -10,6 +10,11 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/DCL41-C/VariablesInsideSwitchStatement.ql b/c/cert/src/rules/DCL41-C/VariablesInsideSwitchStatement.ql
index db42f7102c..6f06174b99 100644
--- a/c/cert/src/rules/DCL41-C/VariablesInsideSwitchStatement.ql
+++ b/c/cert/src/rules/DCL41-C/VariablesInsideSwitchStatement.ql
@@ -10,6 +10,11 @@
* correctness
* maintainability
* readability
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql
index 42f13f6244..f69a78ba2c 100644
--- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql
+++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql
@@ -8,6 +8,11 @@
* @problem.severity warning
* @tags external/cert/id/env30-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql
index a925b80e74..b4d4a74d57 100644
--- a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql
+++ b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/env31-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ENV32-C/ExitHandlersMustReturnNormally.ql b/c/cert/src/rules/ENV32-C/ExitHandlersMustReturnNormally.ql
index 27b6ca2b8e..19cf28b3e9 100644
--- a/c/cert/src/rules/ENV32-C/ExitHandlersMustReturnNormally.ql
+++ b/c/cert/src/rules/ENV32-C/ExitHandlersMustReturnNormally.ql
@@ -8,20 +8,37 @@
* @problem.severity error
* @tags external/cert/id/env32-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
-class ExitFunction extends Function {
- ExitFunction() { this.hasGlobalName(["_Exit", "exit", "quick_exit", "longjmp"]) }
+/**
+ * Exit function or macro.
+ */
+class Exit extends Locatable {
+ Exit() {
+ ["_Exit", "exit", "quick_exit", "longjmp"] = [this.(Function).getName(), this.(Macro).getName()]
+ }
}
-class ExitFunctionCall extends FunctionCall {
- ExitFunctionCall() { this.getTarget() instanceof ExitFunction }
+class ExitExpr extends Expr {
+ ExitExpr() {
+ this.(FunctionCall).getTarget() instanceof Exit
+ or
+ any(MacroInvocation m | this = m.getExpr()).getMacro() instanceof Exit
+ }
}
+/**
+ * Functions that are registered as exit handlers.
+ */
class RegisteredAtexit extends FunctionAccess {
RegisteredAtexit() {
exists(FunctionCall ae |
@@ -32,8 +49,8 @@ class RegisteredAtexit extends FunctionAccess {
}
/**
- * Nodes of type Function, FunctionCall or FunctionAccess that \
- * are reachable from a redistered atexit handler and
+ * Nodes of type Function, FunctionCall, FunctionAccess or ExitExpr
+ * that are reachable from a registered atexit handler and
* can reach an exit function.
*/
class InterestingNode extends ControlFlowNode {
@@ -41,15 +58,17 @@ class InterestingNode extends ControlFlowNode {
exists(Function f |
(
this = f and
- // exit functions are not part of edges
- not this = any(ExitFunction ec)
+ // exit is not part of edges
+ not this instanceof Exit
or
this.(FunctionCall).getTarget() = f
or
this.(FunctionAccess).getTarget() = f
+ or
+ this.(ExitExpr).getEnclosingFunction() = f
) and
- // reaches an exit function
- f.calls*(any(ExitFunction e)) and
+ // reaches an `ExitExpr`
+ f.calls*(any(ExitExpr ee).getEnclosingFunction()) and
// is reachable from a registered atexit function
exists(RegisteredAtexit re | re.getTarget().calls*(f))
)
@@ -62,14 +81,12 @@ class InterestingNode extends ControlFlowNode {
* `Function` and `FunctionCall` in their body.
*/
query predicate edges(InterestingNode a, InterestingNode b) {
- a.(FunctionAccess).getTarget() = b
- or
- a.(FunctionCall).getTarget() = b
- or
+ a.(FunctionAccess).getTarget() = b or
+ a.(FunctionCall).getTarget() = b or
a.(Function).calls(_, b)
}
-from RegisteredAtexit hr, Function f, ExitFunctionCall e
+from RegisteredAtexit hr, Function f, ExitExpr e
where edges(hr, f) and edges+(f, e)
select f, hr, e, "The function is $@ and $@. It must instead terminate by returning.", hr,
"registered as `exit handler`", e, "calls an `exit function`"
diff --git a/c/cert/src/rules/ENV33-C/DoNotCallSystem.ql b/c/cert/src/rules/ENV33-C/DoNotCallSystem.ql
index 58a9c8db79..3b21cd7544 100644
--- a/c/cert/src/rules/ENV33-C/DoNotCallSystem.ql
+++ b/c/cert/src/rules/ENV33-C/DoNotCallSystem.ql
@@ -7,6 +7,11 @@
* @problem.severity error
* @tags external/cert/id/env33-c
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvFunctions.ql b/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvFunctions.ql
index 505f26046a..af54dfa823 100644
--- a/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvFunctions.ql
+++ b/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvFunctions.ql
@@ -9,6 +9,11 @@
* @problem.severity error
* @tags external/cert/id/env34-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvironmentFunWarn.ql b/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvironmentFunWarn.ql
index d46b5b36b8..784b7898d6 100644
--- a/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvironmentFunWarn.ql
+++ b/c/cert/src/rules/ENV34-C/DoNotStorePointersReturnedByEnvironmentFunWarn.ql
@@ -9,6 +9,11 @@
* @problem.severity warning
* @tags external/cert/id/env34-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -16,7 +21,8 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.rules.invalidatedenvstringpointerswarn.InvalidatedEnvStringPointersWarn
-class DoNotStorePointersReturnedByEnvironmentFunWarnQuery extends InvalidatedEnvStringPointersWarnSharedQuery {
+class DoNotStorePointersReturnedByEnvironmentFunWarnQuery extends InvalidatedEnvStringPointersWarnSharedQuery
+{
DoNotStorePointersReturnedByEnvironmentFunWarnQuery() {
this = Contracts2Package::doNotStorePointersReturnedByEnvironmentFunWarnQuery()
}
diff --git a/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql
index cc1dd82bbb..06ac9d1198 100644
--- a/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql
+++ b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/err30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql
index df8519f13f..13f7e40303 100644
--- a/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql
+++ b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql
@@ -8,12 +8,18 @@
* @problem.severity error
* @tags external/cert/id/err30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.c.Errno
+import semmle.code.cpp.dataflow.DataFlow
/**
* A call to an `OutOfBandErrnoSettingFunction`
diff --git a/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql
index dd2e2175f7..8bf583faff 100644
--- a/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql
+++ b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql
@@ -8,13 +8,17 @@
* @problem.severity error
* @tags external/cert/id/err30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.c.Errno
-import semmle.code.cpp.dataflow.DataFlow
/**
* A call to an `OutOfBandErrnoSettingFunction`
diff --git a/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql
index 899fa49e60..a7ccf8c041 100644
--- a/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql
+++ b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql
@@ -7,12 +7,18 @@
* @problem.severity error
* @tags external/cert/id/err30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.c.Errno
+import semmle.code.cpp.dataflow.DataFlow
class SetlocaleFunctionCall extends FunctionCall {
SetlocaleFunctionCall() { this.getTarget().hasGlobalName("setlocale") }
diff --git a/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql
index 1182b30eab..146d0cb30f 100644
--- a/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql
+++ b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql
@@ -7,49 +7,40 @@
* @problem.severity error
* @tags external/cert/id/err32-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.c.Errno
+import codingstandards.c.Signal
import semmle.code.cpp.controlflow.Guards
-
-/**
- * A call to function `signal`
- */
-class SignalCall extends FunctionCall {
- SignalCall() { this.getTarget().hasGlobalName("signal") }
-}
-
-/**
- * A call to `abort` or `_Exit`
- */
-class AbortCall extends FunctionCall {
- AbortCall() { this.getTarget().hasGlobalName(["abort", "_Exit"]) }
-}
+import semmle.code.cpp.dataflow.DataFlow
/**
* A check on `signal` call return value
* `if (signal(SIGINT, handler) == SIG_ERR)`
*/
-class SignalCheckOperation extends EqualityOperation, GuardCondition {
+class SignalCheckOperation extends EqualityOperation instanceof GuardCondition {
BasicBlock errorSuccessor;
SignalCheckOperation() {
this.getAnOperand() = any(MacroInvocation m | m.getMacroName() = "SIG_ERR").getExpr() and
(
this.getOperator() = "==" and
- this.controls(errorSuccessor, true)
+ super.controls(errorSuccessor, true)
or
this.getOperator() = "!=" and
- this.controls(errorSuccessor, false)
+ super.controls(errorSuccessor, false)
)
}
- BasicBlock getCheckedSuccessor() {
- result != errorSuccessor and result = this.getASuccessor()
- }
+ BasicBlock getCheckedSuccessor() { result != errorSuccessor and result = this.getASuccessor() }
BasicBlock getErrorSuccessor() { result = errorSuccessor }
}
@@ -57,12 +48,8 @@ class SignalCheckOperation extends EqualityOperation, GuardCondition {
/**
* Models signal handlers that call signal() and return
*/
-class SignalCallingHandler extends Function {
- SignalCall registration;
-
+class SignalCallingHandler extends SignalHandler {
SignalCallingHandler() {
- // is a signal handler
- this = registration.getArgument(1).(FunctionAccess).getTarget() and
// calls signal() on the handled signal
exists(SignalCall sCall |
sCall.getEnclosingFunction() = this and
@@ -75,8 +62,6 @@ class SignalCallingHandler extends Function {
)
)
}
-
- SignalCall getCall() { result = registration }
}
/**
@@ -100,7 +85,7 @@ where
not isExcluded(errno, Contracts5Package::doNotRelyOnIndeterminateValuesOfErrnoQuery()) and
exists(SignalCallingHandler handler |
// errno read after the handler returns
- handler.getCall() = signal
+ handler.getRegistration() = signal
or
// errno read inside the handler
signal.getEnclosingFunction() = handler
diff --git a/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql
index d7ca143519..5e473b226e 100644
--- a/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql
+++ b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/err33-c
* correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p18
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
@@ -15,6 +20,7 @@ import cpp
import codingstandards.c.cert
import semmle.code.cpp.commons.NULL
import codingstandards.cpp.ReadErrorsAndEOF
+import semmle.code.cpp.dataflow.DataFlow
ComparisonOperation getAValidComparison(string spec) {
spec = "=0" and result.(EqualityOperation).getAnOperand().getValue() = "0"
@@ -441,7 +447,7 @@ ControlFlowNode ferrorNotchecked(FileWriteFunctionCall write) {
not isShortCircuitedEdge(mid, result) and
result = mid.getASuccessor() and
//Stop recursion on call to ferror on the correct file
- not accessSameTarget(result.(FerrorCall).getArgument(0), write.getFileExpr())
+ not sameFileSource(result.(FerrorCall), write)
)
}
diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md
new file mode 100644
index 0000000000..b5a7c98bd6
--- /dev/null
+++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md
@@ -0,0 +1,129 @@
+# EXP16-C: Do not compare function pointers to constant values
+
+This query implements the CERT-C rule EXP16-C:
+
+> Do not compare function pointers to constant values
+
+
+## Description
+
+Comparing a function pointer to a value that is not a null function pointer of the same type will be diagnosed because it typically indicates programmer error and can result in [unexpected behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior). Implicit comparisons will be diagnosed, as well.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, the addresses of the POSIX functions `getuid` and `geteuid` are compared for equality to 0. Because no function address shall be null, the first subexpression will always evaluate to false (0), and the second subexpression always to true (nonzero). Consequently, the entire expression will always evaluate to true, leading to a potential security vulnerability.
+
+```cpp
+/* First the options that are allowed only for root */
+if (getuid == 0 || geteuid != 0) {
+ /* ... */
+}
+
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, the function pointers `getuid` and `geteuid` are compared to 0. This example is from an actual [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) ([VU\#837857](http://www.kb.cert.org/vuls/id/837857)) discovered in some versions of the X Window System server. The vulnerability exists because the programmer neglected to provide the open and close parentheses following the `geteuid()` function identifier. As a result, the `geteuid` token returns the address of the function, which is never equal to 0. Consequently, the `or` condition of this `if` statement is always true, and access is provided to the protected block for all users. Many compilers issue a warning noting such pointless expressions. Therefore, this coding error is normally detected by adherence to [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).
+
+```cpp
+/* First the options that are allowed only for root */
+if (getuid() == 0 || geteuid != 0) {
+ /* ... */
+}
+
+```
+
+## Compliant Solution
+
+The solution is to provide the open and close parentheses following the `geteuid` token so that the function is properly invoked:
+
+```cpp
+/* First the options that are allowed only for root */
+if (getuid() == 0 || geteuid() != 0) {
+ /* ... */
+}
+
+```
+
+## Compliant Solution
+
+A function pointer can be compared to a null function pointer of the same type:
+
+```cpp
+/* First the options that are allowed only for root */
+if (getuid == (uid_t(*)(void))0 || geteuid != (uid_t(*)(void))0) {
+ /* ... */
+}
+
+```
+This code should not be diagnosed by an analyzer.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, the function pointer `do_xyz` is implicitly compared unequal to 0:
+
+```cpp
+int do_xyz(void);
+
+int f(void) {
+/* ... */
+ if (do_xyz) {
+ return -1; /* Indicate failure */
+ }
+/* ... */
+ return 0;
+}
+
+```
+
+## Compliant Solution
+
+In this compliant solution, the function `do_xyz()` is invoked and the return value is compared to 0:
+
+```cpp
+int do_xyz(void);
+
+int f(void) {
+/* ... */
+ if (do_xyz()) {
+ return -1; /* Indicate failure */
+ }
+/* ... */
+ return 0;
+}
+
+```
+
+## Risk Assessment
+
+Errors of omission can result in unintended program flow.
+
+ Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP16-C | Low | Likely | Medium | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 24.04 | function-name-constant-comparison | Partially checked |
Coverity | 2017.07 | BAD_COMPARE | Can detect the specific instance where the address of a function is compared against 0, such as in the case of geteuid versus getuid() in the implementation-specific details |
GCC | 4.3.5 | | Can detect violations of this recommendation when the -Wall flag is used |
Helix QAC | 2024.4 | C0428, C3004, C3344 | |
Klocwork | 2024.4 | CWARN.NULLCHECK.FUNCNAMECWARN.FUNCADDR | |
LDRA tool suite | 9.7.1 | 99 S | Partially implemented |
Parasoft C/C++test | 2024.2 | CERT_C-EXP16-a | Function address should not be compared to zero |
PC-lint Plus | 1.4 | 2440, 2441 | Partially supported: reports address of function, array, or variable directly or indirectly compared to null |
PVS-Studio | 7.35 | V516, V1058 | |
RuleChecker | 24.04 | function-name-constant-comparison | Partially checked |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP16-C).
+
+## Related Guidelines
+
+
+
+
+## Bibliography
+
+ \[ Hatton 1995 \] | Section 2.7.2, "Errors of Omission and Addition" |
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP16-C: Do not compare function pointers to constant values](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql
new file mode 100644
index 0000000000..5f347d817a
--- /dev/null
+++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql
@@ -0,0 +1,73 @@
+/**
+ * @id c/cert/do-not-compare-function-pointers-to-constant-values
+ * @name EXP16-C: Do not compare function pointers to constant values
+ * @description Comparing function pointers to a constant value is not reliable and likely indicates
+ * a programmer error.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/exp16-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/recommendation
+ */
+
+import cpp
+import semmle.code.cpp.controlflow.IRGuards
+import codingstandards.c.cert
+import codingstandards.cpp.types.FunctionType
+import codingstandards.cpp.exprs.FunctionExprs
+import codingstandards.cpp.exprs.Guards
+
+final class FinalElement = Element;
+
+abstract class EffectivelyComparison extends FinalElement {
+ abstract string getExplanation();
+
+ abstract FunctionExpr getFunctionExpr();
+}
+
+final class FinalComparisonOperation = ComparisonOperation;
+
+class ExplicitComparison extends EffectivelyComparison, FinalComparisonOperation {
+ Expr constantExpr;
+ FunctionExpr funcExpr;
+
+ ExplicitComparison() {
+ funcExpr = getAnOperand() and
+ constantExpr = getAnOperand() and
+ exists(constantExpr.getValue()) and
+ not funcExpr = constantExpr and
+ not constantExpr.getExplicitlyConverted().getUnderlyingType() =
+ funcExpr.getExplicitlyConverted().getUnderlyingType()
+ }
+
+ override string getExplanation() { result = "$@ compared to constant value." }
+
+ override FunctionExpr getFunctionExpr() { result = funcExpr }
+}
+
+class ImplicitComparison extends EffectivelyComparison, GuardCondition {
+ ImplicitComparison() {
+ this instanceof FunctionExpr and
+ not getParent() instanceof ComparisonOperation
+ }
+
+ override string getExplanation() { result = "$@ undergoes implicit constant comparison." }
+
+ override FunctionExpr getFunctionExpr() { result = this }
+}
+
+from EffectivelyComparison comparison, FunctionExpr funcExpr, Element function, string funcName
+where
+ not isExcluded(comparison,
+ Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery()) and
+ funcExpr = comparison.getFunctionExpr() and
+ not exists(NullFunctionCallGuard nullGuard | nullGuard.getFunctionExpr() = funcExpr) and
+ function = funcExpr.getFunction() and
+ funcName = funcExpr.describe()
+select comparison, comparison.getExplanation(), function, funcName
diff --git a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql
index fb14515c61..48b9487728 100644
--- a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql
+++ b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql
@@ -8,13 +8,17 @@
* @problem.severity warning
* @tags external/cert/id/exp30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.SideEffect
-import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
diff --git a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql
index c478a3d51e..51b505ec63 100644
--- a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql
+++ b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql
@@ -8,6 +8,11 @@
* @problem.severity warning
* @tags external/cert/id/exp30-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md
new file mode 100644
index 0000000000..aa2dc7036a
--- /dev/null
+++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md
@@ -0,0 +1,104 @@
+# EXP32-C: Do not access a volatile object through a nonvolatile reference
+
+This query implements the CERT-C rule EXP32-C:
+
+> Do not access a volatile object through a nonvolatile reference
+
+
+## Description
+
+An object that has volatile-qualified type may be modified in ways unknown to the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) or have other unknown [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect). Referencing a volatile object by using a non-volatile lvalue is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The C Standard, 6.7.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
+
+
+See [undefined behavior 65](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_65).
+
+## Noncompliant Code Example
+
+In this noncompliant code example, a volatile object is accessed through a non-volatile-qualified reference, resulting in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+```cpp
+#include
+
+void func(void) {
+ static volatile int **ipp;
+ static int *ip;
+ static volatile int i = 0;
+
+ printf("i = %d.\n", i);
+
+ ipp = &ip; /* May produce a warning diagnostic */
+ ipp = (int**) &ip; /* Constraint violation; may produce a warning diagnostic */
+ *ipp = &i; /* Valid */
+ if (*ip != 0) { /* Valid */
+ /* ... */
+ }
+}
+```
+The assignment `ipp = &ip` is not safe because it allows the valid code that follows to reference the value of the volatile object `i` through the non-volatile-qualified reference `ip`. In this example, the compiler may optimize out the entire `if` block because `*ip != 0` must be false if the object to which `ip` points is not volatile.
+
+**Implementation Details**
+
+This example compiles without warning on Microsoft Visual Studio 2013 when compiled in C mode (`/TC`) but causes errors when compiled in C++ mode (`/TP`).
+
+GCC 4.8.1 generates a warning but compiles successfully.
+
+## Compliant Solution
+
+In this compliant solution, `ip` is declared `volatile`:
+
+```cpp
+#include
+
+void func(void) {
+ static volatile int **ipp;
+ static volatile int *ip;
+ static volatile int i = 0;
+
+ printf("i = %d.\n", i);
+
+ ipp = &ip;
+ *ipp = &i;
+ if (*ip != 0) {
+ /* ... */
+ }
+
+}
+```
+
+## Risk Assessment
+
+Accessing an object with a volatile-qualified type through a reference with a non-volatile-qualified type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP32-C | Low | Likely | Medium | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointer-qualifier-cast-volatile pointer-qualifier-cast-volatile-implicit | Supported indirectly via MISRA C 2012 Rule 11.8 |
Axivion Bauhaus Suite | 7.2.0 | CertC-EXP32 | Fully implemented |
Clang | 3.9 | -Wincompatible-pointer-types-discards-qualifiers | |
Compass/ROSE | | | |
Coverity | 2017.07 | MISRA C 2012 Rule 11.8 | Implemented |
GCC | 4.3.5 | | Can detect violations of this rule when the -Wcast-qual flag is used |
Helix QAC | 2022.4 | C0312, C0562, C0563, C0673, C0674 | |
Klocwork | 2022.4 | CERT.EXPR.VOLATILE.ADDR CERT.EXPR.VOLATILE.ADDR.PARAM CERT.EXPR.VOLATILE.PTRPTR | |
LDRA tool suite | 9.7.1 | 344 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-EXP32-a | A cast shall not remove any 'const' or 'volatile' qualification from the type of a pointer or reference |
Polyspace Bug Finder | | CERT C: Rule EXP32-C | Checks for cast to pointer that removes const or volatile qualification (rule fully covered) |
PRQA QA-C | 9.7 | 0312,562,563,673,674 | Fully implemented |
RuleChecker | 22.04 | pointer-qualifier-cast-volatile pointer-qualifier-cast-volatile-implicit | Supported indirectly via MISRA C 2012 Rule 11.8 |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP32-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+In limited cases, this query can raise false-positives for assignment of volatile objects and subsequent accesses of those objects via non-volatile pointers.
+
+## References
+
+* CERT-C: [EXP32-C: Do not access a volatile object through a nonvolatile reference](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql
new file mode 100644
index 0000000000..891b93bcda
--- /dev/null
+++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql
@@ -0,0 +1,95 @@
+/**
+ * @id c/cert/do-not-access-volatile-object-with-non-volatile-reference
+ * @name EXP32-C: Do not access a volatile object through a nonvolatile reference
+ * @description If an an object defined with a volatile-qualified type is referred to with an lvalue
+ * of a non-volatile-qualified type, the behavior is undefined.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp32-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.controlflow.Dereferenced
+
+/**
+ * An expression involving volatile-qualified types that results in undefined behavior.
+ */
+abstract class UndefinedVolatilePointerExpr extends Expr {
+ /**
+ * Gets a descriptive string describing the type of expression and undefined behavior.
+ */
+ abstract string getMessage();
+}
+
+/**
+ * Gets the depth of a pointer's base type's volatile qualifier
+ */
+int getAVolatileDepth(Type type) {
+ type.isVolatile() and result = 1
+ or
+ result = getAVolatileDepth(type.(DerivedType).getBaseType()) + 1
+}
+
+/**
+ * A `Cast` which converts from a pointer to a volatile-qualified type
+ * to a pointer to a non-volatile-qualified type.
+ */
+class CastFromVolatileToNonVolatileBaseType extends Cast, UndefinedVolatilePointerExpr {
+ CastFromVolatileToNonVolatileBaseType() {
+ exists(int i |
+ i = getAVolatileDepth(this.getExpr().getType()) and
+ not i = getAVolatileDepth(this.getActualType())
+ )
+ }
+
+ override string getMessage() {
+ result = "Cast of object with a volatile-qualified type to a non-volatile-qualified type."
+ }
+}
+
+/**
+ * Holds if `va` has a subsequent `VariableAccess` which is dereferenced after access
+ */
+bindingset[va]
+predicate hasSubsequentDereference(VariableAccess va) {
+ dereferenced(pragma[only_bind_out](va).getASuccessor+())
+}
+
+/**
+ * An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and
+ * and *rvalue* that is not also a pointer to a volatile base type.
+ */
+class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVolatilePointerExpr {
+ NonVolatileObjectAssignedToVolatilePointer() {
+ exists(int i |
+ not i = getAVolatileDepth(this.getRValue().getType()) and
+ i = getAVolatileDepth(this.getLValue().(VariableAccess).getTarget().getType())
+ ) and
+ // Checks for subsequent accesses to the underlying object via the original non-volatile
+ // pointer assigned to the volatile pointer. This heuristic can cause false-positives
+ // in certain instances which require more advanced reachability analysis, e.g. loops and scope
+ // considerations that this simple forward traversal of the control-flow graph does not account for.
+ exists(VariableAccess va |
+ va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and
+ hasSubsequentDereference(va)
+ )
+ }
+
+ override string getMessage() {
+ result =
+ "Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer."
+ }
+}
+
+from UndefinedVolatilePointerExpr e
+where not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery())
+select e, e.getMessage()
diff --git a/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.md b/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.md
new file mode 100644
index 0000000000..6328cb86d3
--- /dev/null
+++ b/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.md
@@ -0,0 +1,417 @@
+# EXP33-C: Do not read uninitialized memory
+
+This query implements the CERT-C rule EXP33-C:
+
+> Do not read uninitialized memory
+
+
+## Description
+
+Local, automatic variables assume unexpected values if they are read before they are initialized. The C Standard, 6.7.9, paragraph 10, specifies \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\]
+
+> If an object that has automatic storage duration is not initialized explicitly, its value is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue).
+
+
+See [undefined behavior 11](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_11).
+
+When local, automatic variables are stored on the program stack, for example, their values default to whichever values are currently stored in stack memory.
+
+Additionally, some dynamic memory allocation functions do not initialize the contents of the memory they allocate.
+
+ Function | Initialization |
aligned_alloc() | Does not perform initialization |
calloc() | Zero-initializes allocated memory |
malloc() | Does not perform initialization |
realloc() | Copies contents from original pointer; may not initialize all memory |
+Uninitialized automatic variables or dynamically allocated memory has [indeterminate values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue), which for objects of some types, can be a [trap representation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-traprepresentation). Reading such trap representations is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior); it can cause a program to behave in an [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) manner and provide an avenue for attack. (See [undefined behavior 10](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_10) and [undefined behavior 12](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_12).) In many cases, compilers issue a warning diagnostic message when reading uninitialized variables. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels) for more information.)
+
+
+## Noncompliant Code Example (Return-by-Reference)
+
+In this noncompliant code example, the `set_flag()` function is intended to set the parameter, `sign_flag`, to the sign of `number`. However, the programmer neglected to account for the case where `number` is equal to `0`. Because the local variable `sign` is uninitialized when calling `set_flag()` and is never written to by `set_flag()`, the comparison operation exhibits [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) when reading `sign`.
+
+```cpp
+void set_flag(int number, int *sign_flag) {
+ if (NULL == sign_flag) {
+ return;
+ }
+
+ if (number > 0) {
+ *sign_flag = 1;
+ } else if (number < 0) {
+ *sign_flag = -1;
+ }
+}
+
+int is_negative(int number) {
+ int sign;
+ set_flag(number, &sign);
+ return sign < 0;
+}
+
+```
+Some compilers assume that when the address of an uninitialized variable is passed to a function, the variable is initialized within that function. Because compilers frequently fail to diagnose any resulting failure to initialize the variable, the programmer must apply additional scrutiny to ensure the correctness of the code.
+
+This defect results from a failure to consider all possible data states. (See [MSC01-C. Strive for logical completeness](https://wiki.sei.cmu.edu/confluence/display/c/MSC01-C.+Strive+for+logical+completeness) for more information.)
+
+## Compliant Solution (Return-by-Reference)
+
+This compliant solution trivially repairs the problem by accounting for the possibility that `number` can be equal to 0.
+
+Although compilers and [static analysis](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-staticanalysis) tools often detect uses of uninitialized variables when they have access to the source code, diagnosing the problem is difficult or impossible when either the initialization or the use takes place in object code for which the source code is inaccessible. Unless doing so is prohibitive for performance reasons, an additional defense-in-depth practice worth considering is to initialize local variables immediately after declaration.
+
+```cpp
+void set_flag(int number, int *sign_flag) {
+ if (NULL == sign_flag) {
+ return;
+ }
+
+ /* Account for number being 0 */
+ if (number >= 0) {
+ *sign_flag = 1;
+ } else {
+ *sign_flag = -1;
+ }
+}
+
+int is_negative(int number) {
+ int sign = 0; /* Initialize for defense-in-depth */
+ set_flag(number, &sign);
+ return sign < 0;
+}
+
+```
+
+## Noncompliant Code Example (Uninitialized Local)
+
+In this noncompliant code example, the programmer mistakenly fails to set the local variable `error_log` to the `msg` argument in the `report_error()` function \[[Mercy 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-mercy06)\]. Because `error_log` has not been initialized, an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) is read. The `sprintf()` call copies data from the arbitrary location pointed to by the indeterminate `error_log` variable until a null byte is reached, which can result in a buffer overflow.
+
+```cpp
+#include
+
+/* Get username and password from user, return -1 on error */
+extern int do_auth(void);
+enum { BUFFERSIZE = 24 };
+void report_error(const char *msg) {
+ const char *error_log;
+ char buffer[BUFFERSIZE];
+
+ sprintf(buffer, "Error: %s", error_log);
+ printf("%s\n", buffer);
+}
+
+int main(void) {
+ if (do_auth() == -1) {
+ report_error("Unable to login");
+ }
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example (Uninitialized Local)
+
+In this noncompliant code example, the `report_error()` function has been modified so that `error_log` is properly initialized:
+
+```cpp
+#include
+enum { BUFFERSIZE = 24 };
+void report_error(const char *msg) {
+ const char *error_log = msg;
+ char buffer[BUFFERSIZE];
+
+ sprintf(buffer, "Error: %s", error_log);
+ printf("%s\n", buffer);
+}
+
+```
+This example remains problematic because a buffer overflow will occur if the null-terminated byte string referenced by `msg` is greater than 17 characters, including the null terminator. (See [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator) for more information.)
+
+## Compliant Solution (Uninitialized Local)
+
+In this compliant solution, the buffer overflow is eliminated by calling the `snprintf()` function:
+
+```cpp
+#include
+enum { BUFFERSIZE = 24 };
+void report_error(const char *msg) {
+ char buffer[BUFFERSIZE];
+
+ if (0 < snprintf(buffer, BUFFERSIZE, "Error: %s", msg))
+ printf("%s\n", buffer);
+ else
+ puts("Unknown error");
+}
+
+```
+
+## Compliant Solution (Uninitialized Local)
+
+A less error-prone compliant solution is to simply print the error message directly instead of using an intermediate buffer:
+
+```cpp
+#include
+
+void report_error(const char *msg) {
+ printf("Error: %s\n", msg);
+}
+
+```
+
+## Noncompliant Code Example (mbstate_t)
+
+In this noncompliant code example, the function `mbrlen()` is passed the address of an automatic `mbstate_t` object that has not been properly initialized. This is [undefined behavior 200](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_200) because `mbrlen()` dereferences and reads its third argument.
+
+```cpp
+#include
+#include
+
+void func(const char *mbs) {
+ size_t len;
+ mbstate_t state;
+
+ len = mbrlen(mbs, strlen(mbs), &state);
+}
+
+```
+
+## Compliant Solution (mbstate_t)
+
+Before being passed to a multibyte conversion function, an `mbstate_t` object must be either initialized to the initial conversion state or set to a value that corresponds to the most recent shift state by a prior call to a multibyte conversion function. This compliant solution sets the `mbstate_t` object to the initial conversion state by setting it to all zeros:
+
+```cpp
+#include
+#include
+
+void func(const char *mbs) {
+ size_t len;
+ mbstate_t state;
+
+ memset(&state, 0, sizeof(state));
+ len = mbrlen(mbs, strlen(mbs), &state);
+}
+
+```
+
+## Noncompliant Code Example (POSIX, Entropy)
+
+In this noncompliant code example described in "[More Randomness or Less](http://kqueue.org/blog/2012/06/25/more-randomness-or-less/)" \[[Wang 2012](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Wang12)\], the process ID, time of day, and uninitialized memory `junk` is used to seed a random number generator. This behavior is characteristic of some distributions derived from Debian Linux that use uninitialized memory as a source of entropy because the value stored in `junk` is indeterminate. However, because accessing an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior), compilers may optimize out the uninitialized variable access completely, leaving only the time and process ID and resulting in a loss of desired entropy.
+
+```cpp
+#include
+#include
+#include
+#include
+
+void func(void) {
+ struct timeval tv;
+ unsigned long junk;
+
+ gettimeofday(&tv, NULL);
+ srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk);
+}
+```
+In security protocols that rely on unpredictability, such as RSA encryption, a loss in entropy results in a less secure system.
+
+## Compliant Solution (POSIX, Entropy)
+
+This compliant solution seeds the random number generator by using the CPU clock and the real-time clock instead of reading uninitialized memory:
+
+```cpp
+#include
+#include
+#include
+#include
+
+void func(void) {
+ double cpu_time;
+ struct timeval tv;
+
+ cpu_time = ((double) clock()) / CLOCKS_PER_SEC;
+ gettimeofday(&tv, NULL);
+ srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ cpu_time);
+}
+```
+
+## Noncompliant Code Example (realloc())
+
+The `realloc()` function changes the size of a dynamically allocated memory object. The initial `size` bytes of the returned memory object are unchanged, but any newly added space is uninitialized, and its value is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue). As in the case of `malloc()`, accessing memory beyond the size of the original object is [undefined behavior 181](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_171).
+
+It is the programmer's responsibility to ensure that any memory allocated with `malloc()` and `realloc()` is properly initialized before it is used.
+
+In this noncompliant code example, an array is allocated with `malloc()` and properly initialized. At a later point, the array is grown to a larger size but not initialized beyond what the original array contained. Subsequently accessing the uninitialized bytes in the new array is undefined behavior.
+
+```cpp
+#include
+#include
+enum { OLD_SIZE = 10, NEW_SIZE = 20 };
+
+int *resize_array(int *array, size_t count) {
+ if (0 == count) {
+ return 0;
+ }
+
+ int *ret = (int *)realloc(array, count * sizeof(int));
+ if (!ret) {
+ free(array);
+ return 0;
+ }
+
+ return ret;
+}
+
+void func(void) {
+
+ int *array = (int *)malloc(OLD_SIZE * sizeof(int));
+ if (0 == array) {
+ /* Handle error */
+ }
+
+ for (size_t i = 0; i < OLD_SIZE; ++i) {
+ array[i] = i;
+ }
+
+ array = resize_array(array, NEW_SIZE);
+ if (0 == array) {
+ /* Handle error */
+ }
+
+ for (size_t i = 0; i < NEW_SIZE; ++i) {
+ printf("%d ", array[i]);
+ }
+}
+```
+
+## Compliant Solution (realloc())
+
+In this compliant solution, the `resize_array()` helper function takes a second parameter for the old size of the array so that it can initialize any newly allocated elements:
+
+```cpp
+#include
+#include
+#include
+
+enum { OLD_SIZE = 10, NEW_SIZE = 20 };
+
+int *resize_array(int *array, size_t old_count, size_t new_count) {
+ if (0 == new_count) {
+ return 0;
+ }
+
+ int *ret = (int *)realloc(array, new_count * sizeof(int));
+ if (!ret) {
+ free(array);
+ return 0;
+ }
+
+ if (new_count > old_count) {
+ memset(ret + old_count, 0, (new_count - old_count) * sizeof(int));
+ }
+
+ return ret;
+}
+
+void func(void) {
+
+ int *array = (int *)malloc(OLD_SIZE * sizeof(int));
+ if (0 == array) {
+ /* Handle error */
+ }
+
+ for (size_t i = 0; i < OLD_SIZE; ++i) {
+ array[i] = i;
+ }
+
+ array = resize_array(array, OLD_SIZE, NEW_SIZE);
+ if (0 == array) {
+ /* Handle error */
+ }
+
+ for (size_t i = 0; i < NEW_SIZE; ++i) {
+ printf("%d ", array[i]);
+ }
+}
+```
+
+## Exceptions
+
+**EXP33-C-EX1:** Reading uninitialized memory by an [lvalue](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-lvalue) of type `unsigned char` that could not have been declared with the `register` storage class does not trigger [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The `unsigned char` type is defined to not have a trap representation, which allows for moving bytes without knowing if they are initialized. (See the C Standard, 6.2.6.1, paragraph 3.) The requirement that `register` could not have been used (not merely that it was not used) is because on some architectures, such as the Intel Itanium, registers have a bit to indicate whether or not they have been initialized. The C Standard, 6.3.2.1, paragraph 2, allows such [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) to cause a trap for an object that never had its address taken and is stored in a register if such an object is referred to in any way.
+
+## Risk Assessment
+
+Reading uninitialized variables is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) and can result in [unexpected program behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior). In some cases, these [security flaws](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-securityflaw) may allow the execution of arbitrary code.
+
+Reading uninitialized variables for creating entropy is problematic because these memory accesses can be removed by compiler optimization. [VU\#925211](http://www.kb.cert.org/vuls/id/925211) is an example of a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) caused by this coding error.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP33-C | High | Probable | Medium | P12 | L1 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | uninitialized-local-read uninitialized-variable-use | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-EXP33 | |
CodeSonar | 7.2p0 | LANG.MEM.UVAR | Uninitialized variable |
Compass/ROSE | | | Automatically detects simple violations of this rule, although it may return some false positives. It may not catch more complex violations, such as initialization within functions taking uninitialized variables as arguments. It does catch the second noncompliant code example, and can be extended to catch the first as well |
Coverity | 2017.07 | UNINIT | Implemented |
Cppcheck | 1.66 | uninitvaruninitdatauninitstringuninitMemberVaruninitStructMember | Detects uninitialized variables, uninitialized pointers, uninitialized struct members, and uninitialized array elements (However, if one element is initialized, then cppcheck assumes the array is initialized.) There are FN compared to some other tools because Cppcheck tries to avoid FP in impossible paths. |
GCC | 4.3.5 | | Can detect some violations of this rule when the -Wuninitialized flag is used |
Helix QAC | 2022.4 | DF2726, DF2727, DF2728, DF2961, DF2962, DF2963, DF2966, DF2967, DF2968, DF2971, DF2972, DF2973, DF2976, DF2977, DF2978 | |
Klocwork | 2022.4 | UNINIT.HEAP.MIGHT UNINIT.HEAP.MUST UNINIT.STACK.ARRAY.MIGHT UNINIT.STACK.ARRAY.MUST UNINIT.STACK.ARRAY.PARTIAL.MUST UNINIT.STACK.MIGHT UNINIT.STACK.MUST UNINIT.CTOR.MIGHT UNINIT.CTOR.MUST | |
LDRA tool suite | 9.7.1 | 53 D, 69 D, 631 S, 652 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-EXP33-a | Avoid use before initialization |
Parasoft Insure++ | 2022.2 | | Runtime analysis |
PC-lint Plus | 1.4 | 530, 603, 644, 901 | Fully supported |
Polyspace Bug Finder | R2022b | CERT C: Rule EXP33-C | Checks for: Non-initialized variableon-initialized variable, non-initialized pointeron-initialized pointer. Rule partially covered |
PRQA QA-C | 9.7 | 2726, 2727, 2728, 2961, 2962, 2963, 2966, 2967, 2968, 2971, 2972, 2973, 2976, 2977, 2978 | Fully implemented |
PRQA QA-C++ | 4.4 | 2961, 2962, 2963, 2966, 2967, 2968, 2971, 2972, 2973, 2976, 2977, 2978 | |
PVS-Studio | 7.22 | V573 , V614 , V670 , V679 , V1050 | |
RuleChecker | 22.04 | uninitialized-local-read | Partially checked |
Splint | 3.1.1 | | |
TrustInSoft Analyzer | 1.38 | initialisation | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+[CVE-2009-1888](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1888) results from a violation of this rule. Some versions of SAMBA (up to 3.3.5) call a function that takes in two potentially uninitialized variables involving access rights. An attacker can [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit) these coding errors to bypass the access control list and gain access to protected files \[[xorl 2009](http://xorl.wordpress.com/2009/06/26/cve-2009-1888-samba-acls-uninitialized-memory-read/)\].
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP33-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-119 and EXP33-C**
+
+* Intersection( CWE-119, EXP33-C) = Ø
+* EXP33-C is about reading uninitialized memory, but this memory is considered part of a valid buffer (on the stack, or returned by a heap function). No buffer overflow is involved.
+**CWE-676 and EXP33-C**
+* Intersection( CWE-676, EXP33-C) = Ø
+* EXP33-C implies that memory allocation functions (e.g., malloc()) are dangerous because they do not initialize the memory they reserve. However, the danger is not in their invocation, but rather reading their returned memory without initializing it.
+**CWE-758 and EXP33-C**
+
+Independent( INT34-C, INT36-C, MSC37-C, FLP32-C, EXP33-C, EXP30-C, ERR34-C, ARR32-C)
+
+CWE-758 = Union( EXP33-C, list) where list =
+
+* Undefined behavior that results from anything other than reading uninitialized memory
+**CWE-665 and EXP33-C**
+
+Intersection( CWE-665, EXP33-C) = Ø
+
+CWE-665 is about correctly initializing items (usually objects), not reading them later. EXP33-C is about reading memory later (that has not been initialized).
+
+**CWE-908 and EXP33-C**
+
+CWE-908 = Union( EXP33-C, list) where list =
+
+* Use of uninitialized items besides raw memory (objects, disk space, etc)
+New CWE-CERT mappings:
+
+**CWE-123 and EXP33-C**
+
+Intersection( CWE-123, EXP33-C) = Ø
+
+EXP33-C is only about reading uninitialized memory, not writing, whereas CWE-123 is about writing.
+
+**CWE-824 and EXP33-C**
+
+EXP33-C = Union( CWE-824, list) where list =
+
+* Read of uninitialized memory that does not represent a pointer
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP33-C: Do not read uninitialized memory](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.ql b/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.ql
new file mode 100644
index 0000000000..94deea912e
--- /dev/null
+++ b/c/cert/src/rules/EXP33-C/DoNotReadUninitializedMemory.ql
@@ -0,0 +1,28 @@
+/**
+ * @id c/cert/do-not-read-uninitialized-memory
+ * @name EXP33-C: Do not read uninitialized memory
+ * @description Using the value of an object with automatic storage duration while it is
+ * indeterminate is undefined behavior.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/exp33-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory
+
+class DoNotReadUninitializedMemoryQuery extends ReadOfUninitializedMemorySharedQuery {
+ DoNotReadUninitializedMemoryQuery() {
+ this = InvalidMemory1Package::doNotReadUninitializedMemoryQuery()
+ }
+}
diff --git a/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.md b/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.md
new file mode 100644
index 0000000000..40b0d59a4a
--- /dev/null
+++ b/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.md
@@ -0,0 +1,219 @@
+# EXP34-C: Do not dereference null pointers
+
+This query implements the CERT-C rule EXP34-C:
+
+> Do not dereference null pointers
+
+
+## Description
+
+Dereferencing a null pointer is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+On many platforms, dereferencing a null pointer results in [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination), but this is not required by the standard. See "[Clever Attack Exploits Fully-Patched Linux Kernel](http://www.theregister.co.uk/2009/07/17/linux_kernel_exploit/)" \[[Goodin 2009](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Goodin2009)\] for an example of a code execution [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit) that resulted from a null pointer dereference.
+
+## Noncompliant Code Example
+
+This noncompliant code example is derived from a real-world example taken from a vulnerable version of the `libpng` library as deployed on a popular ARM-based cell phone \[[Jack 2007](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Jack07)\]. The `libpng` library allows applications to read, create, and manipulate PNG (Portable Network Graphics) raster image files. The `libpng` library implements its own wrapper to `malloc()` that returns a null pointer on error or on being passed a 0-byte-length argument.
+
+This code also violates [ERR33-C. Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors).
+
+```cpp
+#include /* From libpng */
+#include
+
+void func(png_structp png_ptr, int length, const void *user_data) {
+ png_charp chunkdata;
+ chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+ /* ... */
+ memcpy(chunkdata, user_data, length);
+ /* ... */
+ }
+```
+If `length` has the value `−1`, the addition yields 0, and `png_malloc()` subsequently returns a null pointer, which is assigned to `chunkdata`. The `chunkdata` pointer is later used as a destination argument in a call to `memcpy()`, resulting in user-defined data overwriting memory starting at address 0. In the case of the ARM and XScale architectures, the `0x0` address is mapped in memory and serves as the exception vector table; consequently, dereferencing `0x0` did not cause an [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination).
+
+## Compliant Solution
+
+This compliant solution ensures that the pointer returned by `png_malloc()` is not null. It also uses the unsigned type `size_t` to pass the `length` parameter, ensuring that negative values are not passed to `func()`.
+
+This solution also ensures that the `user_data` pointer is not null. Passing a null pointer to memcpy() would produce undefined behavior, even if the number of bytes to copy were 0. The `user_data` pointer could be invalid in other ways, such as pointing to freed memory. However there is no portable way to verify that the pointer is valid, other than checking for null.
+
+```cpp
+#include /* From libpng */
+#include
+
+ void func(png_structp png_ptr, size_t length, const void *user_data) {
+ png_charp chunkdata;
+ if (length == SIZE_MAX) {
+ /* Handle error */
+ }
+ if (NULL == user_data) {
+ /* Handle error */
+ }
+ chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+ if (NULL == chunkdata) {
+ /* Handle error */
+ }
+ /* ... */
+ memcpy(chunkdata, user_data, length);
+ /* ... */
+
+ }
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, `input_str` is copied into dynamically allocated memory referenced by `c_str`. If `malloc()` fails, it returns a null pointer that is assigned to `c_str`. When `c_str` is dereferenced in `memcpy()`, the program exhibits [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Additionally, if `input_str` is a null pointer, the call to `strlen()` dereferences a null pointer, also resulting in undefined behavior. This code also violates [ERR33-C. Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors).
+
+```cpp
+#include
+#include
+
+void f(const char *input_str) {
+ size_t size = strlen(input_str) + 1;
+ char *c_str = (char *)malloc(size);
+ memcpy(c_str, input_str, size);
+ /* ... */
+ free(c_str);
+ c_str = NULL;
+ /* ... */
+}
+```
+
+## Compliant Solution
+
+This compliant solution ensures that both `input_str` and the pointer returned by `malloc()` are not null:
+
+```cpp
+#include
+#include
+
+void f(const char *input_str) {
+ size_t size;
+ char *c_str;
+
+ if (NULL == input_str) {
+ /* Handle error */
+ }
+
+ size = strlen(input_str) + 1;
+ c_str = (char *)malloc(size);
+ if (NULL == c_str) {
+ /* Handle error */
+ }
+ memcpy(c_str, input_str, size);
+ /* ... */
+ free(c_str);
+ c_str = NULL;
+ /* ... */
+}
+```
+
+## Noncompliant Code Example
+
+This noncompliant code example is from a version of `drivers/net/tun.c` and affects Linux kernel 2.6.30 \[[Goodin 2009](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Goodin2009)\]:
+
+```cpp
+static unsigned int tun_chr_poll(struct file *file, poll_table *wait) {
+ struct tun_file *tfile = file->private_data;
+ struct tun_struct *tun = __tun_get(tfile);
+ struct sock *sk = tun->sk;
+ unsigned int mask = 0;
+
+ if (!tun)
+ return POLLERR;
+
+ DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name);
+
+ poll_wait(file, &tun->socket.wait, wait);
+
+ if (!skb_queue_empty(&tun->readq))
+ mask |= POLLIN | POLLRDNORM;
+
+ if (sock_writeable(sk) ||
+ (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
+ sock_writeable(sk)))
+ mask |= POLLOUT | POLLWRNORM;
+
+ if (tun->dev->reg_state != NETREG_REGISTERED)
+ mask = POLLERR;
+
+ tun_put(tun);
+ return mask;
+}
+
+```
+The `sk` pointer is initialized to `tun->sk` before checking if `tun` is a null pointer. Because null pointer dereferencing is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior), the compiler (GCC in this case) can optimize away the `if (!tun)` check because it is performed after `tun->sk` is accessed, implying that `tun` is non-null. As a result, this noncompliant code example is vulnerable to a null pointer dereference exploit, because null pointer dereferencing can be permitted on several platforms, for example, by using `mmap(2)` with the `MAP_FIXED` flag on Linux and Mac OS X, or by using the `shmat()` POSIX function with the `SHM_RND` flag \[[Liu 2009](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Liu2009)\].
+
+## Compliant Solution
+
+This compliant solution eliminates the null pointer deference by initializing `sk` to `tun->sk` following the null pointer check. It also adds assertions to document that certain other pointers must not be null.
+
+```cpp
+static unsigned int tun_chr_poll(struct file *file, poll_table *wait) {
+ assert(file);
+ struct tun_file *tfile = file->private_data;
+ struct tun_struct *tun = __tun_get(tfile);
+ struct sock *sk;
+ unsigned int mask = 0;
+
+ if (!tun)
+ return POLLERR;
+ assert(tun->dev);
+ sk = tun->sk;
+ assert(sk);
+ assert(sk->socket);
+ /* The remaining code is omitted because it is unchanged... */
+}
+
+```
+
+## Risk Assessment
+
+Dereferencing a null pointer is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior), typically [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). In some situations, however, dereferencing a null pointer can lead to the execution of arbitrary code \[[Jack 2007](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Jack07), [van Sprundel 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-vanSprundel06)\]. The indicated severity is for this more severe case; on platforms where it is not possible to exploit a null pointer dereference to execute arbitrary code, the actual severity is low.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP34-C | High | Likely | Medium | P18 | L1 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | null-dereferencing | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-EXP34 | |
CodeSonar | 7.2p0 | LANG.MEM.NPD LANG.STRUCT.NTAD LANG.STRUCT.UPD | Null pointer dereference Null test after dereference Unchecked parameter dereference |
Compass/ROSE | | | Can detect violations of this rule. In particular, ROSE ensures that any pointer returned by malloc() , calloc() , or realloc() is first checked for NULL before being used (otherwise, it is free() -ed). ROSE does not handle cases where an allocation is assigned to an lvalue that is not a variable (such as a struct member or C++ function call returning a reference) |
Coverity | 2017.07 | CHECKED_RETURN NULL_RETURNS REVERSE_INULL FORWARD_NULL | Finds instances where a pointer is checked against NULL and then later dereferenced Identifies functions that can return a null pointer but are not checked Identifies code that dereferences a pointer and then checks the pointer against NULL Can find the instances where NULL is explicitly dereferenced or a pointer is checked against NULL but then dereferenced anyway. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary |
Cppcheck | 1.66 | nullPointer, nullPointerDefaultArg, nullPointerRedundantCheck | Context sensitive analysis Detects when NULL is dereferenced (Array of pointers is not checked. Pointer members in structs are not checked.) Finds instances where a pointer is checked against NULL and then later dereferenced Identifies code that dereferences a pointer and then checks the pointer against NULL Does not guess that return values from malloc() , strchr() , etc., can be NULL (The return value from malloc() is NULL only if there is OOMo and the dev might not care to handle that. The return value from strchr() is often NULL , but the dev might know that a specific strchr() function call will not return NULL .) |
Helix QAC | 2022.4 | DF2810, DF2811, DF2812, DF2813 | |
Klocwork | 2022.4 | NPD.CHECK.CALL.MIGHT NPD.CHECK.CALL.MUST NPD.CHECK.MIGHT NPD.CHECK.MUST NPD.CONST.CALL NPD.CONST.DEREF NPD.FUNC.CALL.MIGHT NPD.FUNC.CALL.MUST NPD.FUNC.MIGHT NPD.FUNC.MUST NPD.GEN.CALL.MIGHT NPD.GEN.CALL.MUST NPD.GEN.MIGHT NPD.GEN.MUST RNPD.CALL RNPD.DEREF | |
LDRA tool suite | 9.7.1 | 45 D, 123 D, 128 D, 129 D, 130 D, 131 D, 652 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-EXP34-a | Avoid null pointer dereferencing |
Parasoft Insure++ | | | Runtime analysis |
PC-lint Plus | 1.4 | 413, 418, 444, 613, 668 | Partially supported |
Polyspace Bug Finder | | CERT C: Rule EXP34-C | Checks for use of null pointers (rule partially covered) |
PRQA QA-C | 9.7 | 2810, 2811, 2812, 2813 | Fully implemented |
PRQA QA-C++ | 4.4 | 2810, 2811, 2812, 2813 | |
PVS-Studio | 7.22 | V522 , V595 , V664 , V713 , V1004 | |
SonarQube C/C++ Plugin | 3.11 | S2259 | |
Splint | 3.1.1 | | |
TrustInSoft Analyzer | 1.38 | mem_access | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP34-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-690 and EXP34-C**
+
+EXP34-C = Union( CWE-690, list) where list =
+
+* Dereferencing null pointers that were not returned by a function
+**CWE-252 and EXP34-C**
+
+Intersection( CWE-252, EXP34-C) = Ø
+
+EXP34-C is a common consequence of ignoring function return values, but it is a distinct error, and can occur in other scenarios too.
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP34-C: Do not dereference null pointers](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.ql b/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.ql
new file mode 100644
index 0000000000..51b93c8000
--- /dev/null
+++ b/c/cert/src/rules/EXP34-C/DoNotDereferenceNullPointers.ql
@@ -0,0 +1,26 @@
+/**
+ * @id c/cert/do-not-dereference-null-pointers
+ * @name EXP34-C: Do not dereference null pointers
+ * @description Dereferencing a null pointer leads to undefined behavior.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/exp34-c
+ * correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p18
+ * external/cert/level/l1
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.dereferenceofnullpointer.DereferenceOfNullPointer
+
+class DoNotDereferenceNullPointersQuery extends DereferenceOfNullPointerSharedQuery {
+ DoNotDereferenceNullPointersQuery() {
+ this = InvalidMemory1Package::doNotDereferenceNullPointersQuery()
+ }
+}
diff --git a/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.md b/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.md
new file mode 100644
index 0000000000..58ff1b03cf
--- /dev/null
+++ b/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.md
@@ -0,0 +1,225 @@
+# EXP35-C: Do not modify objects with temporary lifetime
+
+This query implements the CERT-C rule EXP35-C:
+
+> Do not modify objects with temporary lifetime
+
+
+## Description
+
+The C11 Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] introduced a new term: *temporary lifetime*. Modifying an object with temporary lifetime is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). According to subclause 6.2.4, paragraph 8
+
+> A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and *temporary*lifetime. Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
+
+
+This definition differs from the C99 Standard (which defines modifying the result of a function call or accessing it after the next sequence point as undefined behavior) because a temporary object's lifetime ends when the evaluation containing the full expression or full declarator ends, so the result of a function call can be accessed. This extension to the lifetime of a temporary also removes a quiet change to C90 and improves compatibility with C++.
+
+C functions may not return arrays; however, functions can return a pointer to an array or a `struct` or `union` that contains arrays. Consequently, in any version of C, if a function call returns by value a `struct` or `union` containing an array, do not modify those arrays within the expression containing the function call. In C99 and older, do not access an array returned by a function after the next sequence point or after the evaluation of the containing full expression or full declarator ends.
+
+## Noncompliant Code Example
+
+This noncompliant code example [conforms](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-conformingprogram) to the C11 Standard; however, it fails to conform to C99. If compiled with a C99-conforming implementation, this code has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) because the sequence point preceding the call to `printf()` comes between the call and the access by `printf()` of the string in the returned object.
+
+```cpp
+#include
+
+struct X { char a[8]; };
+
+struct X salutation(void) {
+ struct X result = { "Hello" };
+ return result;
+}
+
+struct X addressee(void) {
+ struct X result = { "world" };
+ return result;
+}
+
+int main(void) {
+ printf("%s, %s!\n", salutation().a, addressee().a);
+ return 0;
+}
+
+```
+
+## Compliant Solution (C11 and newer)
+
+This compliant solution checks `__STDC_VERSION__` to ensure that a pre-C11 compiler will fail to compile the code, rather than invoking undefined behavior.
+
+```cpp
+#include
+
+#if __STDC_VERSION__ < 201112L
+#error This code requires a compiler supporting the C11 standard or newer
+#endif
+
+struct X { char a[8]; };
+
+struct X salutation(void) {
+ struct X result = { "Hello" };
+ return result;
+}
+
+struct X addressee(void) {
+ struct X result = { "world" };
+ return result;
+}
+
+int main(void) {
+ printf("%s, %s!\n", salutation().a, addressee().a);
+ return 0;
+}
+```
+
+## Compliant Solution
+
+This compliant solution stores the structures returned by the call to `addressee()` before calling the `printf()` function. Consequently, this program conforms to both C99 and C11.
+
+```cpp
+#include
+
+struct X { char a[8]; };
+
+struct X salutation(void) {
+ struct X result = { "Hello" };
+ return result;
+}
+
+struct X addressee(void) {
+ struct X result = { "world" };
+ return result;
+}
+
+int main(void) {
+ struct X my_salutation = salutation();
+ struct X my_addressee = addressee();
+
+ printf("%s, %s!\n", my_salutation.a, my_addressee.a);
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example
+
+This noncompliant code example attempts to retrieve an array and increment the array's first value. The array is part of a `struct` that is returned by a function call. Consequently, the array has temporary lifetime, and modifying the array is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) in both C99 and C11.
+
+```cpp
+#include
+
+struct X { int a[6]; };
+
+struct X addressee(void) {
+ struct X result = { { 1, 2, 3, 4, 5, 6 } };
+ return result;
+}
+
+int main(void) {
+ printf("%x", ++(addressee().a[0]));
+ return 0;
+}
+
+```
+
+## Compliant Solution
+
+This compliant solution stores the structure returned by the call to `addressee()` as `my_x` before calling the `printf()` function. When the array is modified, its lifetime is no longer temporary but matches the lifetime of the block in `main()`.
+
+```cpp
+#include
+
+struct X { int a[6]; };
+
+struct X addressee(void) {
+ struct X result = { { 1, 2, 3, 4, 5, 6 } };
+ return result;
+}
+
+int main(void) {
+ struct X my_x = addressee();
+ printf("%x", ++(my_x.a[0]));
+ return 0;
+}
+
+```
+
+## Noncompliant Code Example
+
+This noncompliant code example attempts to save a pointer to an array that is part of a `struct` that is returned by a function call. Consequently, the array has temporary lifetime, and using the pointer to it outside of the full expression is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) in both C99 and C11.
+
+```cpp
+#include
+
+struct X { int a[6]; };
+
+struct X addressee(void) {
+ struct X result = { { 1, 2, 3, 4, 5, 6 } };
+ return result;
+}
+
+int main(void) {
+ int *my_a = addressee().a;
+ printf("%x", my_a[0]);
+ return 0;
+}
+
+```
+
+## Compliant Solution
+
+This compliant solution stores the structure returned by the call to `addressee()` as `my_x` before saving a pointer to its array member. When the pointer is used, its lifetime is no longer temporary but matches the lifetime of the block in `main()`.
+
+```cpp
+#include
+
+struct X { int a[6]; };
+
+struct X addressee(void) {
+ struct X result = { { 1, 2, 3, 4, 5, 6 } };
+ return result;
+}
+
+int main(void) {
+ struct X my_x = addressee();
+ int *my_a = my_x.a;
+ printf("%x", my_a[0]);
+ return 0;
+}
+
+```
+
+## Risk Assessment
+
+Attempting to modify an array or access it after its lifetime expires may result in erroneous program behavior.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP35-C | Low | Probable | Medium | P4 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | temporary-object-modification | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-EXP35 | |
Helix QAC | 2022.4 | C0450, C0455, C0459, C0464, C0465 C++3807, C++3808 | |
LDRA tool suite | 9.7.1 | 642 S, 42 D, 77 D | Enhanced Enforcement |
Parasoft C/C++test | 2022.2 | CERT_C-EXP35-a | Do not modify objects with temporary lifetime |
Polyspace Bug Finder | R2023a | CERT-C: Rule EXP35-C | Checks for accesses on objects with temporary lifetime (rule fully covered) |
PRQA QA-C | 9.7 | 0450 \[U\], 0455 \[U\], 0459 \[U\], 0464 \[U\], 0465 \[U\] | |
Splint | 3.1.1 | | |
RuleChecker | 22.04 | temporary-object-modification | Partially checked |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP35-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+ Taxonomy | Taxonomy item | Relationship |
ISO/IEC TR 24772:2013 | Dangling References to Stack Frames \[DCM\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Side-effects and Order of Evaluation \[SAM\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+This implementation also always reports non-modifying accesses of objects with temporary lifetime, which are only compliant in C11.
+
+## References
+
+* CERT-C: [EXP35-C: Do not modify objects with temporary lifetime](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.ql b/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.ql
new file mode 100644
index 0000000000..3f7d9ae142
--- /dev/null
+++ b/c/cert/src/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.ql
@@ -0,0 +1,30 @@
+/**
+ * @id c/cert/do-not-modify-objects-with-temporary-lifetime
+ * @name EXP35-C: Do not modify objects with temporary lifetime
+ * @description Attempting to modify an object with temporary lifetime results in undefined
+ * behavior.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp35-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.c.Objects
+
+// Note: Undefined behavior is possible regardless of whether the accessed field from the returned
+// struct is an array or a scalar (i.e. arithmetic and pointer types) member, according to the standard.
+from FieldAccess fa, TemporaryObjectIdentity tempObject
+where
+ not isExcluded(fa, InvalidMemory2Package::doNotModifyObjectsWithTemporaryLifetimeQuery()) and
+ fa.getQualifier().getUnconverted() = tempObject
+select fa, "Field access on $@ qualifier occurs after its temporary object lifetime.", tempObject,
+ "temporary object"
diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md
new file mode 100644
index 0000000000..4a682f4afe
--- /dev/null
+++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md
@@ -0,0 +1,222 @@
+# EXP36-C: Do not cast pointers into more strictly aligned pointer types
+
+This query implements the CERT-C rule EXP36-C:
+
+> Do not cast pointers into more strictly aligned pointer types
+
+
+## Description
+
+Do not convert a pointer value to a pointer type that is more strictly aligned than the referenced type. Different alignments are possible for different types of objects. If the type-checking system is overridden by an explicit cast or the pointer is converted to a void pointer (`void *`) and then to a different type, the alignment of an object may be changed.
+
+The C Standard, 6.3.2.3, paragraph 7 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
+
+
+See [undefined behavior 25.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_25)
+
+If the misaligned pointer is dereferenced, the program may [terminate abnormally](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). On some architectures, the cast alone may cause a loss of information even if the value is not dereferenced if the types involved have differing alignment requirements.
+
+## Noncompliant Code Example
+
+In this noncompliant example, the `char` pointer `&c` is converted to the more strictly aligned `int` pointer `ip`. On some [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation), `cp` will not match `&c`. As a result, if a pointer to one object type is converted to a pointer to a different object type, the second object type must not require stricter alignment than the first.
+
+```cpp
+#include
+
+void func(void) {
+ char c = 'x';
+ int *ip = (int *)&c; /* This can lose information */
+ char *cp = (char *)ip;
+
+ /* Will fail on some conforming implementations */
+ assert(cp == &c);
+}
+
+```
+
+## Compliant Solution (Intermediate Object)
+
+In this compliant solution, the `char` value is stored into an object of type `int` so that the pointer's value will be properly aligned:
+
+```cpp
+#include
+
+void func(void) {
+ char c = 'x';
+ int i = c;
+ int *ip = &i;
+
+ assert(ip == &i);
+}
+```
+
+## Noncompliant Code Example
+
+The C Standard allows any object pointer to be cast to and from `void *`. As a result, it is possible to silently convert from one pointer type to another without the compiler diagnosing the problem by storing or casting a pointer to `void *` and then storing or casting it to the final type. In this noncompliant code example, `loop_function()` is passed the `char` pointer `char_ptr` but returns an object of type `int` pointer:
+
+```cpp
+int *loop_function(void *v_pointer) {
+ /* ... */
+ return v_pointer;
+}
+
+void func(char *char_ptr) {
+ int *int_ptr = loop_function(char_ptr);
+
+ /* ... */
+}
+```
+This example compiles without warning using GCC 4.8 on Ubuntu Linux 14.04. However, `int_pointer` can be more strictly aligned than an object of type `char *`.
+
+## Compliant Solution
+
+Because the input parameter directly influences the return value, and `loop_function()` returns an object of type `int *`, the formal parameter `v_pointer` is redeclared to accept only an object of type `int *`:
+
+```cpp
+int *loop_function(int *v_pointer) {
+ /* ... */
+ return v_pointer;
+}
+
+void func(int *loop_ptr) {
+ int *int_ptr = loop_function(loop_ptr);
+
+ /* ... */
+}
+```
+
+## Noncompliant Code Example
+
+Some architectures require that pointers are correctly aligned when accessing objects larger than a byte. However, it is common in system code that unaligned data (for example, the network stacks) must be copied to a properly aligned memory location, such as in this noncompliant code example:
+
+```cpp
+#include
+
+struct foo_header {
+ int len;
+ /* ... */
+};
+
+void func(char *data, size_t offset) {
+ struct foo_header *tmp;
+ struct foo_header header;
+
+ tmp = (struct foo_header *)(data + offset);
+ memcpy(&header, tmp, sizeof(header));
+
+ /* ... */
+}
+```
+Assigning an unaligned value to a pointer that references a type that needs to be aligned is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). An [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) may notice, for example, that `tmp` and `header` must be aligned and use an inline `memcpy()` that uses instructions that assume aligned data.
+
+## Compliant Solution
+
+This compliant solution avoids the use of the `foo_header` pointer:
+
+```cpp
+#include
+
+struct foo_header {
+ int len;
+ /* ... */
+};
+
+void func(char *data, size_t offset) {
+ struct foo_header header;
+ memcpy(&header, data + offset, sizeof(header));
+
+ /* ... */
+}
+```
+
+## Exceptions
+
+**EXP36-C-EX1:** Some hardware architectures have relaxed requirements with regard to pointer alignment. Using a pointer that is not properly aligned is correctly handled by the architecture, although there might be a performance penalty. On such an architecture, improper pointer alignment is permitted but remains an efficiency problem.
+
+The x86 32- and 64-bit architectures usually impose only a performance penalty for violations of this rule, but under some circumstances, noncompliant code can still exhibit undefined behavior. Consider the following program:
+
+```cpp
+#include
+#include
+
+#define READ_UINT16(ptr) (*(uint16_t *)(ptr))
+#define WRITE_UINT16(ptr, val) (*(uint16_t *)(ptr) = (val))
+
+void compute(unsigned char *b1, unsigned char *b2,
+ int value, int range) {
+ int i;
+ for (i = 0; i < range; i++) {
+ int newval = (int)READ_UINT16(b1) + value;
+ WRITE_UINT16(b2, newval);
+ b1 += 2;
+ b2 += 2;
+ }
+}
+
+int main() {
+ unsigned char buffer1[1024];
+ unsigned char buffer2[1024];
+ printf("Compute something\n");
+ compute(buffer1 + 3, buffer2 + 1, 42, 500);
+ return 0;
+}
+```
+This code tries to read short ints (which are 16 bits long) from odd pairs in a character array, which violates this rule. On 32- and 64-bit x86 platforms, this program should run to completion without incident. However, the program aborts with a SIGSEGV due to the unaligned reads on a 64-bit platform running Debian Linux, when compiled with GCC 4.9.4 using the flags `-O3` or `-O2 -ftree-loop-vectorize -fvect-cost-model`.
+
+If a developer wishes to violate this rule and use undefined behavior, they must not only ensure that the hardware guarantees the behavior of the object code, but they must also ensure that their compiler, along with its optimizer, also respect these guarantees.
+
+**EXP36-C-EX2**: If a pointer is known to be correctly aligned to the target type, then a cast to that type is permitted. There are several cases where a pointer is known to be correctly aligned to the target type. The pointer could point to an object declared with a suitable alignment specifier. It could point to an object returned by `aligned_alloc()`, `calloc()`, `malloc()`, or `realloc()`, as per the C standard, section 7.22.3, paragraph 1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\].
+
+This compliant solution uses the alignment specifier, which is new to C11, to declare the `char` object `c` with the same alignment as that of an object of type `int`. As a result, the two pointers reference equally aligned pointer types:
+
+```cpp
+#include
+#include
+
+void func(void) {
+ /* Align c to the alignment of an int */
+ alignas(int) char c = 'x';
+ int *ip = (int *)&c;
+ char *cp = (char *)ip;
+ /* Both cp and &c point to equally aligned objects */
+ assert(cp == &c);
+}
+```
+
+## Risk Assessment
+
+Accessing a pointer or an object that is not properly aligned can cause a program to crash or give erroneous information, or it can cause slow pointer accesses (if the architecture allows misaligned accesses).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP36-C | Low | Probable | Medium | P4 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointer-cast-alignment | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-EXP36 | |
CodeSonar | 7.2p0 | LANG.CAST.PC.OBJ | Cast: Object Pointers |
Compass/ROSE | | | Can detect violations of this rule. However, it does not flag explicit casts to void \* and then back to another pointer type |
Coverity | 2017.07 | MISRA C 2004 Rule 11.4 MISRA C 2012 Rule 11.1 MISRA C 2012 Rule 11.2 MISRA C 2012 Rule 11.5 MISRA C 2012 Rule 11.7 | Implemented |
ECLAIR | 1.2 | CC2.EXP36 | Fully implemented |
EDG | | | |
GCC | 4.3.5 | | Can detect some violations of this rule when the -Wcast-align flag is used |
Helix QAC | 2022.4 | C0326, C3305 C++3033, C++3038 | |
Klocwork | 2022.4 | MISRA.CAST.OBJ_PTR_TO_OBJ_PTR.2012 | |
LDRA tool suite | 9.7.1 | 94 S, 606 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-EXP36-a | A cast should not be performed between a pointer to object type and a different pointer to object type |
PC-lint Plus | 1.4 | 2445 | Partially supported: reports casts directly from a pointer to a less strictly aligned type to a pointer to a more strictly aligned type |
Polyspace Bug Finder | R2022b | CERT C: Rule EXP36-C | Checks for source buffer misaligned with destination buffer (rule fully covered) |
PRQA QA-C | 9.7 | 0326, 3305 | Fully implemented |
PRQA QA-C++ | 4.4 | 3033, 3038 | |
PVS-Studio | 7.22 | V548 , V641 , V1032 | |
RuleChecker | 22.04 | pointer-cast-alignment | Fully checked |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP36-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP36-C: Do not cast pointers into more strictly aligned pointer types](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql
new file mode 100644
index 0000000000..0d294e48b1
--- /dev/null
+++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql
@@ -0,0 +1,188 @@
+/**
+ * @id c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type
+ * @name EXP36-C: Do not cast pointers into more strictly aligned pointer types
+ * @description Converting a pointer to a different type results in undefined behavior if the
+ * pointer is not correctly aligned for the new type.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp36-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.Alignment
+import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import ExprWithAlignmentToCStyleCastFlow::PathGraph
+
+/**
+ * An expression with a type that has defined alignment requirements
+ */
+abstract class ExprWithAlignment extends Expr {
+ /**
+ * Gets the alignment requirements in bytes for the underlying `Expr`
+ */
+ abstract int getAlignment();
+
+ /**
+ * Gets a descriptive string describing the type of expression
+ */
+ abstract string getKind();
+}
+
+/**
+ * A class extending `AddressOfExpr` and `ExprWithAlignment` to reason about the
+ * alignment of base types addressed with C address-of expressions
+ */
+class AddressOfAlignedVariableExpr extends AddressOfExpr, ExprWithAlignment {
+ AddressOfAlignedVariableExpr() { this.getAddressable() instanceof Variable }
+
+ AlignAs getAlignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() }
+
+ override int getAlignment() {
+ result = getAlignAsAttribute().getArgument(0).getValueInt()
+ or
+ result = getAlignAsAttribute().getArgument(0).getValueType().getSize()
+ or
+ not exists(getAlignAsAttribute()) and
+ result = this.getAddressable().(Variable).getType().getAlignment()
+ }
+
+ override string getKind() { result = "address-of expression" }
+}
+
+/**
+ * A class extending `FunctionCall` and `ExprWithAlignment` to reason about the
+ * alignment of pointers allocated with calls to C standard library allocation functions
+ */
+class DefinedAlignmentAllocationExpr extends FunctionCall, ExprWithAlignment {
+ int alignment;
+
+ DefinedAlignmentAllocationExpr() {
+ this.getTarget().getName() = "aligned_alloc" and
+ lowerBound(this.getArgument(0)) = upperBound(this.getArgument(0)) and
+ alignment = upperBound(this.getArgument(0))
+ or
+ this.getTarget().getName() = ["malloc", "calloc", "realloc"] and
+ alignment = getGlobalMaxAlignT()
+ }
+
+ override int getAlignment() { result = alignment }
+
+ override string getKind() { result = "call to " + this.getTarget().getName() }
+}
+
+/**
+ * An `Expr` of type `PointerType` but not `VoidPointerType`
+ * which is the unique non-`Conversion` expression of a `Cast`.
+ */
+class UnconvertedCastFromNonVoidPointerExpr extends Expr {
+ UnconvertedCastFromNonVoidPointerExpr() {
+ exists(CStyleCast cast |
+ cast.getUnconverted() = this and
+ this.getUnspecifiedType() instanceof PointerType and
+ not this.getUnspecifiedType() instanceof VoidPointerType
+ )
+ }
+}
+
+/**
+ * A class extending `UnconvertedCastFromNonVoidPointerExpr` and `ExprWithAlignment` to reason
+ * about the alignment of pointers accessed based solely on the pointers' base types.
+ */
+class DefaultAlignedPointerExpr extends UnconvertedCastFromNonVoidPointerExpr, ExprWithAlignment {
+ DefaultAlignedPointerExpr() {
+ not AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprFlow::flowTo(DataFlow::exprNode(this))
+ }
+
+ override int getAlignment() { result = this.getType().(PointerType).getBaseType().getAlignment() }
+
+ override string getKind() {
+ result =
+ "pointer base type " +
+ this.getType().(PointerType).getBaseType().getUnspecifiedType().getName()
+ }
+}
+
+/**
+ * A data-flow configuration for tracking flow from `AddressOfExpr` which provide
+ * most reliable or explicitly defined alignment information to the less reliable
+ * `DefaultAlignedPointerAccessExpr` expressions.
+ *
+ * This data-flow configuration is used
+ * to exclude an `DefaultAlignedPointerAccessExpr` as a source if a preceding source
+ * defined by this configuration provides more accurate alignment information.
+ */
+module AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprConfig implements
+ DataFlow::ConfigSig
+{
+ predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof AddressOfAlignedVariableExpr or
+ source.asExpr() instanceof DefinedAlignmentAllocationExpr
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() instanceof UnconvertedCastFromNonVoidPointerExpr
+ }
+}
+
+module AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprFlow =
+ DataFlow::Global;
+
+/**
+ * A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions
+ * to casts which perform pointer type conversions and potentially create pointer alignment issues.
+ */
+module ExprWithAlignmentToCStyleCastConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ExprWithAlignment }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(CStyleCast cast |
+ cast.getUnderlyingType() instanceof PointerType and
+ cast.getUnconverted() = sink.asExpr()
+ )
+ }
+
+ predicate isBarrierOut(DataFlow::Node node) {
+ // the default interprocedural data-flow model flows through any array assignment expressions
+ // to the qualifier (array base or pointer dereferenced) instead of the individual element
+ // that the assignment modifies. this default behaviour causes false positives for any future
+ // cast of the array base, so remove the assignment edge at the expense of false-negatives.
+ exists(AssignExpr a |
+ node.asExpr() = a.getRValue() and
+ (
+ a.getLValue() instanceof ArrayExpr or
+ a.getLValue() instanceof PointerDereferenceExpr
+ )
+ )
+ }
+}
+
+module ExprWithAlignmentToCStyleCastFlow = DataFlow::Global;
+
+from
+ ExprWithAlignmentToCStyleCastFlow::PathNode source,
+ ExprWithAlignmentToCStyleCastFlow::PathNode sink, ExprWithAlignment expr, CStyleCast cast,
+ Type toBaseType, int alignmentFrom, int alignmentTo
+where
+ not isExcluded(cast, Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery()) and
+ ExprWithAlignmentToCStyleCastFlow::flowPath(source, sink) and
+ source.getNode().asExpr() = expr and
+ sink.getNode().asExpr() = cast.getUnconverted() and
+ toBaseType = cast.getActualType().(PointerType).getBaseType() and
+ alignmentTo = toBaseType.getAlignment() and
+ alignmentFrom = expr.getAlignment() and
+ // flag cases where the cast's target type has stricter alignment requirements than the source
+ alignmentFrom < alignmentTo
+select cast, source, sink,
+ "Cast from pointer with " + alignmentFrom +
+ "-byte alignment (defined by $@) to pointer with base type " + toBaseType.getUnderlyingType() +
+ " with " + alignmentTo + "-byte alignment.", expr.getUnconverted(), expr.getKind()
diff --git a/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql
index ad8520e321..a6e633d7f6 100644
--- a/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql
+++ b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/exp37-c
* correctness
* security
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql
index 17e1c0e0e9..6d223dab72 100644
--- a/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql
+++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql
@@ -8,13 +8,18 @@
* @problem.severity error
* @tags external/cert/id/exp37-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import semmle.code.cpp.dataflow.DataFlow
-import DataFlow::PathGraph
+import SuspectFunctionPointerToCallFlow::PathGraph
/**
* An expression of type `FunctionPointer` which is the unconverted expression of a cast
@@ -37,26 +42,26 @@ class SuspiciousFunctionPointerCastExpr extends Expr {
* Data-flow configuration for flow from a `SuspiciousFunctionPointerCastExpr`
* to a call of the function pointer resulting from the function pointer cast
*/
-class SuspectFunctionPointerToCallConfig extends DataFlow::Configuration {
- SuspectFunctionPointerToCallConfig() { this = "SuspectFunctionPointerToCallConfig" }
-
- override predicate isSource(DataFlow::Node src) {
+module SuspectFunctionPointerToCallConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node src) {
src.asExpr() instanceof SuspiciousFunctionPointerCastExpr
}
- override predicate isSink(DataFlow::Node sink) {
+ predicate isSink(DataFlow::Node sink) {
exists(VariableCall call | sink.asExpr() = call.getExpr().(VariableAccess))
}
}
+module SuspectFunctionPointerToCallFlow = DataFlow::Global;
+
from
- SuspectFunctionPointerToCallConfig config, DataFlow::PathNode src, DataFlow::PathNode sink,
+ SuspectFunctionPointerToCallFlow::PathNode src, SuspectFunctionPointerToCallFlow::PathNode sink,
Access access
where
not isExcluded(src.getNode().asExpr(),
ExpressionsPackage::doNotCallFunctionPointerWithIncompatibleTypeQuery()) and
access = src.getNode().asExpr() and
- config.hasFlowPath(src, sink)
+ SuspectFunctionPointerToCallFlow::flowPath(src, sink)
select src, src, sink,
"Incompatible function $@ assigned to function pointer is eventually called through the pointer.",
access.getTarget(), access.getTarget().getName()
diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql
index e76c62ee2d..4c5ba57504 100644
--- a/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql
+++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/exp37-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md
new file mode 100644
index 0000000000..cd01c4282e
--- /dev/null
+++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md
@@ -0,0 +1,312 @@
+# EXP39-C: Do not access a variable through a pointer of an incompatible type
+
+This query implements the CERT-C rule EXP39-C:
+
+> Do not access a variable through a pointer of an incompatible type
+
+
+## Description
+
+Modifying a variable through a pointer of an incompatible type (other than `unsigned char`) can lead to unpredictable results. Subclause 6.2.7 of the C Standard states that two types may be distinct yet compatible and addresses precisely when two distinct types are compatible.
+
+This problem is often caused by a violation of aliasing rules. The C Standard, 6.5, paragraph 7 \[ [ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011) \], specifies those circumstances in which an object may or may not be aliased.
+
+> An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
+
+
+* a type compatible with the effective type of the object,
+* a qualified version of a type compatible with the effective type of the object,
+* a type that is the signed or unsigned type corresponding to the effective type of the object,
+* a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
+* an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
+* a character type.
+Accessing an object by means of any other [lvalue](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-lvalue) expression (other than `unsigned char`) is [undefined behavior 37](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_37).
+
+## Noncompliant Code Example
+
+In this noncompliant example, an object of type `float` is incremented through an `int *`. The programmer can use the unit in the last place to get the next representable value for a floating-point type. However, accessing an object through a pointer of an incompatible type is undefined behavior.
+
+```cpp
+#include
+
+void f(void) {
+ if (sizeof(int) == sizeof(float)) {
+ float f = 0.0f;
+ int *ip = (int *)&f;
+ (*ip)++;
+ printf("float is %f\n", f);
+ }
+}
+
+```
+
+## Compliant Solution
+
+In this compliant solution, the standard C function `nextafterf()` is used to round toward the highest representable floating-point value:
+
+```cpp
+#include
+#include
+#include
+
+void f(void) {
+ float f = 0.0f;
+ f = nextafterf(f, FLT_MAX);
+ printf("float is %f\n", f);
+}
+
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, an array of two values of type `short` is treated as an integer and assigned an integer value. The resulting values are indeterminate.
+
+```cpp
+#include
+
+void func(void) {
+ short a[2];
+ a[0]=0x1111;
+ a[1]=0x1111;
+
+ *(int *)a = 0x22222222;
+
+ printf("%x %x\n", a[0], a[1]);
+}
+```
+When translating this code, an implementation can assume that no access through an integer pointer can change the array `a`, consisting of shorts. Consequently, `printf()` may be called with the original values of `a[0]` and `a[1]`.
+
+**Implementation Details**
+
+Recent versions of GCC turn on the option `-fstrict-aliasing,` which allows alias-based optimizations, by default with `-O2`. Some architectures then print "1111 1111" as a result. Without optimization, the executable generates the *expected* output "2222 2222."
+
+To disable optimizations based on alias analysis for faulty legacy code, the option `-fno-strict-aliasing` can be used as a workaround. The option `-Wstrict-aliasing,` which is included in `-Wall,` warns about some, but not all, violations of aliasing rules when `-fstrict-aliasing` is active.
+
+When GCC 3.4.6 compiles this code with optimization, the assignment through the aliased pointer is effectively eliminated.
+
+## Compliant Solution
+
+This compliant solution uses a `union` type that includes a type compatible with the effective type of the object:
+
+```cpp
+#include
+
+void func(void) {
+ union {
+ short a[2];
+ int i;
+ } u;
+
+ u.a[0]=0x1111;
+ u.a[1]=0x1111;
+ u.i = 0x22222222;
+
+ printf("%x %x\n", u.a[0], u.a[1]);
+
+ /* ... */
+}
+```
+The C standard states:
+
+> If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called “type punning”). This might be a trap representation.
+
+
+The call to `printf()` typically outputs "2222 2222". However, there is no guarantee that this will be true; the object representations of `a` and `i` are unspecified and need not be compatible in this way, despite this operation being commonly accepted as an implementation extension. (See [unspecified behavior 11](https://wiki.sei.cmu.edu/confluence/display/c/DD.+Unspecified+Behavior#DD.UnspecifiedBehavior-unspecifiedbehavior11).)
+
+## Noncompliant Code Example
+
+In this noncompliant code example, a `gadget` object is allocated, then `realloc()` is called to create a `widget` object using the memory from the `gadget` object. Although reusing memory to change types is acceptable, accessing the memory copied from the original object is undefined behavior.
+
+```cpp
+#include
+
+struct gadget {
+ int i;
+ double d;
+ char *p;
+};
+
+struct widget {
+ char *q;
+ int j;
+ double e;
+};
+
+void func(void) {
+ struct gadget *gp;
+ struct widget *wp;
+
+ gp = (struct gadget *)malloc(sizeof(struct gadget));
+ if (!gp) {
+ /* Handle error */
+ }
+ /* ... Initialize gadget ... */
+ wp = (struct widget *)realloc(gp, sizeof(struct widget));
+ if (!wp) {
+ free(gp);
+ /* Handle error */
+ }
+ if (wp->j == 12) {
+ /* ... */
+ }
+ /* ... */
+ free(wp);
+}
+```
+
+## Compliant Solution
+
+This compliant solution reuses the memory from the `gadget` object but reinitializes the memory to a consistent state before reading from it:
+
+```cpp
+#include
+#include
+
+struct gadget {
+ int i;
+ double d;
+ char *p;
+};
+
+struct widget {
+ char *q;
+ int j;
+ double e;
+};
+
+void func(void) {
+ struct gadget *gp;
+ struct widget *wp;
+
+ gp = (struct gadget *)malloc(sizeof (struct gadget));
+ if (!gp) {
+ /* Handle error */
+ }
+ /* ... */
+ wp = (struct widget *)realloc(gp, sizeof(struct widget));
+ if (!wp) {
+ free(gp);
+ /* Handle error */
+ }
+ memset(wp, 0, sizeof(struct widget));
+ /* ... Initialize widget ... */
+
+ if (wp->j == 12) {
+ /* ... */
+ }
+ /* ... */
+ free(wp);
+}
+```
+
+## Noncompliant Code Example
+
+According to the C Standard, 6.7.6.2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], using two or more incompatible arrays in an expression is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 76](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_76).)
+
+For two array types to be compatible, both should have compatible underlying element types, and both size specifiers should have the same constant value. If either of these properties is violated, the resulting behavior is undefined.
+
+In this noncompliant code example, the two arrays `a` and `b` fail to satisfy the equal size specifier criterion for array compatibility. Because `a` and `b` are not equal, writing to what is believed to be a valid member of `a` might exceed its defined memory boundary, resulting in an arbitrary memory overwrite.
+
+```cpp
+enum { ROWS = 10, COLS = 15 };
+
+void func(void) {
+ int a[ROWS][COLS];
+ int (*b)[ROWS] = a;
+}
+```
+Most compilers will produce a warning diagnostic if the two array types used in an expression are incompatible.
+
+## Compliant Solution
+
+In this compliant solution, `b` is declared to point to an array with the same number of elements as `a`, satisfying the size specifier criterion for array compatibility:
+
+```cpp
+enum { ROWS = 10, COLS = 15 };
+
+void func(void) {
+ int a[ROWS][COLS];
+ int (*b)[COLS] = a;
+}
+```
+
+## Risk Assessment
+
+Optimizing for performance can lead to aliasing errors that can be quite difficult to detect. Furthermore, as in the preceding example, unexpected results can lead to buffer overflow attacks, bypassing security checks, or unexpected execution.
+
+ Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP39-C | Medium | Unlikely | High | P2 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Helix QAC | 2022.4 | C0310, C0751, C3305 C++3017, C++3030, C++3033 | |
Klocwork | 2022.4 | MISRA.CAST.FUNC_PTR.2012 MISRA.CAST.INCOMPLETE_PTR_TO_ANY.2012 MISRA.CAST.OBJ_PTR_TO_NON_INT.2012 MISRA.CAST.OBJ_PTR_TO_OBJ_PTR.2012 | |
LDRA tool suite | 9.7.1 | 94 S, 554 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-EXP39-a CERT_C-EXP39-b CERT_C-EXP39-c CERT_C-EXP39-d CERT_C-EXP39-e CERT_C-EXP39-f | There shall be no implicit conversions from integral to floating type A cast should not be performed between a pointer to object type and a different pointer to object type Avoid accessing arrays and pointers out of bounds Avoid buffer overflow from tainted data due to defining incorrect format limits Avoid buffer read overflow from tainted data Avoid buffer write overflow from tainted data |
Polyspace Bug Finder | R2022b | CERT C: Rule EXP39-C | Checks for cast to pointer pointing to object of different type (rule partially covered) |
PRQA QA-C | 9.7 | 0310, 0751, 3305 | Partially implemented |
PRQA QA-C++ | 4.4 | 3017, 3030, 3033 | |
PVS-Studio | 7.22 | V580 | |
+
+
+## Related Vulnerabilities
+
+Search for vulnerabilities resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP39-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+ Taxonomy | Taxonomy item | Relationship |
ISO/IEC TS 17961 | Accessing an object through a pointer to an incompatible type \[ptrcomp\] | Prior to 2018-01-12: CERT: Unspecified Relationship |
CWE 2.11 | CWE-119 , Improper Restriction of Operations within the Bounds of a Memory Buffer | 2017-05-18: CERT: Partial overlap |
CWE 2.11 | CWE-125 , Out-of-bounds Read | 2017-05-18: CERT: Partial overlap |
CWE 2.11 | CWE-704 | 2017-06-14: CERT: Rule subset of CWE |
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-119 and EXP39-C**
+
+Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C) STR31-C = Subset( Union( ARR30-C, ARR38-C)) STR32-C = Subset( ARR38-C)
+
+Intersection( EXP39-C, CWE-119) =
+
+* Reading memory assigned to one type, but being accessed through a pointer to a larger type.
+EXP39-C – CWE-119 =
+* Writing to memory assigned to one type, but accessed through a pointer to a larger type
+* Reading memory assigned to one type, but being accessed through a pointer to a smaller (or equal-sized) type
+CWE-119 – EXP39-C =
+* Reading beyond a buffer using a means other than accessing a variable through an incompatible pointer.
+**CWE-123 and EXP39-C**
+
+Intersection( CWE-123, EXP39-C) = Ø
+
+EXP39-C allows overflowing a (small) buffer, but not arbitrary memory writes. (Possibly an arbitrary-memory write exploit could be devised using a “perfect storm” of incompatible types, but this would be uncommon in practice.)
+
+**CWE-125 and EXP39-C**
+
+Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C) STR31-C = Subset( Union( ARR30-C, ARR38-C)) STR32-C = Subset( ARR38-C)
+
+Intersection( EXP39-C, CWE-125) =
+
+* Reading memory assigned to one type, but being accessed through a pointer to a larger type.
+ESP39-C – CWE-125 =
+* Reading memory assigned to one type, but being accessed through a pointer to a smaller (or equal-sized) type
+CWE-125 – EXP39-C =
+* Reading beyond a buffer using a means other than accessing a variable through an incompatible pointer.
+**CWE-188 and EXP39-C**
+
+Intersection( CWE-188, EXP39-C) = Ø
+
+CWE-188 appears to be about making assumptions about the layout of memory between distinct variables (that are not part of a larger struct or array). Such assumptions typically involve pointer arithmetic (which violates ARR30-C). EXP39-C involves only one object in memory being (incorrectly) interpreted as if it were another object. EG a float being treated as an int (usually via pointers and typecasting)
+
+**CWE-704 and EXP39-C**
+
+CWE-704 = Union( EXP39-C, list) where list =
+
+* Incorrect (?) typecast that is not incompatible
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP39-C: Do not access a variable through a pointer of an incompatible type](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql
new file mode 100644
index 0000000000..856cad1d58
--- /dev/null
+++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql
@@ -0,0 +1,209 @@
+/**
+ * @id c/cert/do-not-access-variable-via-pointer-of-incompatible-type
+ * @name EXP39-C: Do not access a variable through a pointer of an incompatible type
+ * @description Modifying underlying pointer data through a pointer of an incompatible type can lead
+ * to unpredictable results.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp39-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.controlflow.Dominance
+import IndirectCastFlow::PathGraph
+
+/**
+ * The standard function `memset` and its assorted variants
+ */
+class MemsetFunction extends Function {
+ MemsetFunction() {
+ this.hasGlobalOrStdOrBslName("memset")
+ or
+ this.hasGlobalOrStdName("wmemset")
+ or
+ this.hasGlobalName(["__builtin_memset", "__builtin___memset_chk", "__builtin_memset_chk"])
+ }
+}
+
+class IndirectCastAnalysisUnconvertedCastExpr extends Expr {
+ IndirectCastAnalysisUnconvertedCastExpr() { this = any(Cast c).getUnconverted() }
+}
+
+class IndirectCastAnalysisDereferenceSink extends Expr {
+ IndirectCastAnalysisDereferenceSink() { dereferenced(this) }
+}
+
+class ReallocationFunction extends AllocationFunction {
+ ReallocationFunction() { exists(this.getReallocPtrArg()) }
+}
+
+newtype IndirectCastFlowState =
+ /**
+ * A data-flow state for a pointer which has not been reallocated.
+ */
+ IndirectCastDefaultFlowState() or
+ /**
+ * A data-flow state for a pointer which has been reallocated but
+ * has not yet been zeroed with a memset call.
+ */
+ IndirectCastReallocatedFlowState()
+
+/**
+ * A data-flow configuration to track the flow from cast expressions to either
+ * other cast expressions or to dereferences of pointers reallocated with a call
+ * to `realloc` but not cleared via a function call to `memset`.
+ */
+module IndirectCastConfig implements DataFlow::StateConfigSig {
+ class FlowState = IndirectCastFlowState;
+
+ predicate isSource(DataFlow::Node source, FlowState state) {
+ state instanceof IndirectCastDefaultFlowState and
+ source.asExpr() instanceof IndirectCastAnalysisUnconvertedCastExpr
+ }
+
+ predicate isSink(DataFlow::Node sink, FlowState state) {
+ sink.asExpr() instanceof IndirectCastAnalysisUnconvertedCastExpr and
+ state instanceof IndirectCastDefaultFlowState
+ or
+ sink.asExpr() instanceof IndirectCastAnalysisDereferenceSink and
+ state instanceof IndirectCastReallocatedFlowState and
+ // The memset call won't always have an edge to subsequent dereferences.
+ //
+ // Therefore, check that:
+ // 1) The memset call dominates the dereference.
+ // 2) The realloc call dominates the memset call.
+ // 3) There is no subsequent memset that also dominates the dereference.
+ //
+ // Currently, there is no relation between the pointer passed to memset
+ // and the pointer dereferenced. This unimplemented check might produce
+ // false-negatives when the memset call is unrelated to the reallocated memory.
+ not exists(FunctionCall memset, FunctionCall realloc, Expr ptr |
+ memset.getTarget() instanceof MemsetFunction and
+ realloc.getTarget() instanceof ReallocationFunction and
+ ptr = sink.asExpr() and
+ dominates(memset, ptr) and
+ not dominates(memset,
+ any(FunctionCall other |
+ other.getTarget() instanceof MemsetFunction and
+ other != memset and
+ dominates(other, ptr)
+ |
+ other
+ )) and
+ dominates(realloc, memset)
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node, FlowState state) {
+ state instanceof IndirectCastReallocatedFlowState and
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof MemsetFunction and
+ fc.getArgument(0) = node.asExpr()
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
+ ) {
+ // track pointer flow through realloc calls and update state to `IndirectCastReallocatedFlowState`
+ state1 instanceof IndirectCastDefaultFlowState and
+ state2 instanceof IndirectCastReallocatedFlowState and
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof ReallocationFunction and
+ node1.asExpr() = fc.getArgument(fc.getTarget().(ReallocationFunction).getReallocPtrArg()) and
+ node2.asExpr() = fc
+ )
+ or
+ // track pointer flow through memset calls and reset state to `IndirectCastDefaultFlowState`
+ state1 instanceof IndirectCastReallocatedFlowState and
+ state2 instanceof IndirectCastDefaultFlowState and
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof MemsetFunction and
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
+}
+
+module IndirectCastFlow = DataFlow::GlobalWithState;
+
+pragma[inline]
+predicate areTypesSameExceptForConstSpecifiers(Type a, Type b) {
+ a.stripType() = b.stripType() and
+ a.getSize() = b.getSize() and
+ forall(Specifier s | s = a.getASpecifier() and not s.hasName("const") |
+ b.hasSpecifier(s.getName())
+ )
+}
+
+pragma[inline]
+Type compatibleTypes(Type type) {
+ not (
+ type.isVolatile() and not result.isVolatile()
+ or
+ type.isConst() and not result.isConst()
+ ) and
+ (
+ (
+ // all types are compatible with void and explicitly-unsigned char types
+ result instanceof UnsignedCharType or
+ [result.stripTopLevelSpecifiers(), type.stripTopLevelSpecifiers()] instanceof VoidType
+ )
+ or
+ not result instanceof UnsignedCharType and
+ not result instanceof VoidType and
+ (
+ type.stripType() instanceof Struct and
+ type.getUnspecifiedType() = result.getUnspecifiedType() and
+ not type.(Struct).isAnonymous() and
+ not result.(Struct).isAnonymous()
+ or
+ not type.stripType() instanceof Struct and
+ (
+ areTypesSameExceptForConstSpecifiers(type, result)
+ or
+ result.getSize() = type.getSize() and
+ (
+ type instanceof Enum and result instanceof IntegralOrEnumType
+ or
+ not type instanceof PlainCharType and
+ (
+ result.(IntegralType).isSigned() and type.(IntegralType).isSigned()
+ or
+ result.(IntegralType).isUnsigned() and type.(IntegralType).isUnsigned()
+ )
+ or
+ result.(FloatingPointType).getDomain() = type.(FloatingPointType).getDomain()
+ )
+ or
+ type instanceof Enum and result instanceof IntegralOrEnumType
+ )
+ )
+ )
+}
+
+from
+ IndirectCastFlow::PathNode source, IndirectCastFlow::PathNode sink, Cast cast, Type fromType,
+ Type toType
+where
+ not isExcluded(sink.getNode().asExpr(),
+ Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery()) and
+ cast.getFile().compiledAsC() and
+ IndirectCastFlow::flowPath(source, sink) and
+ // include only sinks which are not a compatible type to the associated source
+ source.getNode().asExpr() = cast.getUnconverted() and
+ fromType = cast.getUnconverted().getType().(PointerType).getBaseType() and
+ toType = sink.getNode().asExpr().getActualType().(PointerType).getBaseType() and
+ not toType = compatibleTypes(fromType)
+select sink.getNode().asExpr().getUnconverted(), source, sink,
+ "Cast from " + fromType + " to " + toType + " results in an incompatible pointer base type."
diff --git a/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.md b/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.md
new file mode 100644
index 0000000000..6effa8e7c4
--- /dev/null
+++ b/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.md
@@ -0,0 +1,95 @@
+# EXP40-C: Do not modify constant objects
+
+This query implements the CERT-C rule EXP40-C:
+
+> Do not modify constant objects
+
+
+## Description
+
+The C Standard, 6.7.3, paragraph 6 \[[IS](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)[O/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> If an attempt is made to modify an object defined with a `const`-qualified type through use of an [lvalue](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-lvalue) with non-`const`-qualified type, the behavior is undefined.
+
+
+See also [undefined behavior 64](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_64).
+
+There are existing compiler [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) that allow `const`-qualified objects to be modified without generating a warning message.
+
+Avoid casting away `const` qualification because doing so makes it possible to modify `const`-qualified objects without issuing diagnostics. (See [EXP05-C. Do not cast away a const qualification](https://wiki.sei.cmu.edu/confluence/display/c/EXP05-C.+Do+not+cast+away+a+const+qualification) and [STR30-C. Do not attempt to modify string literals](https://wiki.sei.cmu.edu/confluence/display/c/STR30-C.+Do+not+attempt+to+modify+string+literals) for more details.)
+
+## Noncompliant Code Example
+
+This noncompliant code example allows a constant object to be modified:
+
+```cpp
+const int **ipp;
+int *ip;
+const int i = 42;
+
+void func(void) {
+ ipp = &ip; /* Constraint violation */
+ *ipp = &i; /* Valid */
+ *ip = 0; /* Modifies constant i (was 42) */
+}
+```
+The first assignment is unsafe because it allows the code that follows it to attempt to change the value of the `const` object `i`.
+
+**Implementation Details**
+
+If `ipp`, `ip`, and `i` are declared as automatic variables, this example compiles without warning with Microsoft Visual Studio 2013 when compiled in C mode (`/TC`) and the resulting program changes the value of `i`. GCC 4.8.1 generates a warning but compiles, and the resulting program changes the value of `i`.
+
+If `ipp`, `ip`, and `i` are declared with static storage duration, this program compiles without warning and terminates abnormally with Microsoft Visual Studio 2013, and compiles with warning and terminates abnormally with GCC 4.8.1.
+
+## Compliant Solution
+
+The compliant solution depends on the intent of the programmer. If the intent is that the value of `i` is modifiable, then it should not be declared as a constant, as in this compliant solution:
+
+```cpp
+int **ipp;
+int *ip;
+int i = 42;
+
+void func(void) {
+ ipp = &ip; /* Valid */
+ *ipp = &i; /* Valid */
+ *ip = 0; /* Valid */
+}
+```
+If the intent is that the value of i is not meant to change, then do not write noncompliant code that attempts to modify it.
+
+## Risk Assessment
+
+Modifying constant objects through nonconstant references is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP40-C | Low | Unlikely | Medium | P2 | L3 |
+
+
+## Automated Detection
+
+
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP40-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+The implementation does not consider pointer aliasing via multiple indirection.
+
+## References
+
+* CERT-C: [EXP40-C: Do not modify constant objects](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.ql b/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.ql
new file mode 100644
index 0000000000..9d8e4b16d4
--- /dev/null
+++ b/c/cert/src/rules/EXP40-C/DoNotModifyConstantObjects.ql
@@ -0,0 +1,66 @@
+/**
+ * @id c/cert/do-not-modify-constant-objects
+ * @name EXP40-C: Do not modify constant objects
+ * @description Do not modify constant objects. This may result in undefined behavior.
+ * @kind path-problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp40-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.dataflow.DataFlow
+import CastFlow::PathGraph
+import codingstandards.cpp.SideEffect
+
+class ConstRemovingCast extends Cast {
+ ConstRemovingCast() {
+ this.getExpr().getType().(DerivedType).getBaseType*().isConst() and
+ not this.getType().(DerivedType).getBaseType*().isConst()
+ }
+}
+
+class MaybeReturnsStringLiteralFunctionCall extends FunctionCall {
+ MaybeReturnsStringLiteralFunctionCall() {
+ getTarget().getName() in [
+ "strpbrk", "strchr", "strrchr", "strstr", "wcspbrk", "wcschr", "wcsrchr", "wcsstr",
+ "memchr", "wmemchr"
+ ]
+ }
+}
+
+module CastConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ source.asExpr().getFullyConverted() instanceof ConstRemovingCast
+ or
+ source.asExpr().getFullyConverted() = any(MaybeReturnsStringLiteralFunctionCall c)
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand()
+ }
+}
+
+module CastFlow = DataFlow::Global;
+
+from CastFlow::PathNode src, CastFlow::PathNode sink
+where
+ CastFlow::flowPath(src, sink)
+ or
+ sink.getNode()
+ .asExpr()
+ .(VariableEffect)
+ .getTarget()
+ .getType()
+ .(DerivedType)
+ .getBaseType*()
+ .isConst()
+select sink, src, sink, "Const variable assigned with non const-value."
diff --git a/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.md b/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.md
new file mode 100644
index 0000000000..297b718852
--- /dev/null
+++ b/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.md
@@ -0,0 +1,134 @@
+# EXP42-C: Do not compare padding data
+
+This query implements the CERT-C rule EXP42-C:
+
+> Do not compare padding data
+
+
+## Description
+
+The C Standard, 6.7.2.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> There may be unnamed padding within a structure object, but not at its beginning. . . . There may be unnamed padding at the end of a structure or union.
+
+
+Subclause 6.7.9, paragraph 9, states that
+
+> unnamed members of objects of structure and union type do not participate in initialization. Unnamed members of structure objects have indeterminate value even after initialization.
+
+
+The only exception is that padding bits are set to zero when a static or thread-local object is implicitly initialized (paragraph10):
+
+> If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized explicitly, then:
+
+
+— if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
+
+— if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
+
+Because these padding values are unspecified, attempting a byte-by-byte comparison between structures can lead to incorrect results \[[Summit 1995](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Summit95)\].
+
+## Noncompliant Code Example
+
+In this noncompliant code example, `memcmp()` is used to compare the contents of two structures, including any padding bytes:
+
+```cpp
+#include
+
+struct s {
+ char c;
+ int i;
+ char buffer[13];
+};
+
+void compare(const struct s *left, const struct s *right) {
+ if ((left && right) &&
+ (0 == memcmp(left, right, sizeof(struct s)))) {
+ /* ... */
+ }
+}
+```
+
+## Compliant Solution
+
+In this compliant solution, all of the fields are compared manually to avoid comparing any padding bytes:
+
+```cpp
+#include
+
+struct s {
+ char c;
+ int i;
+ char buffer[13];
+};
+
+void compare(const struct s *left, const struct s *right) {
+ if ((left && right) &&
+ (left->c == right->c) &&
+ (left->i == right->i) &&
+ (0 == memcmp(left->buffer, right->buffer, 13))) {
+ /* ... */
+ }
+}
+```
+
+## Exceptions
+
+**EXP42-C-EX1**: A structure can be defined such that the members are aligned properly or the structure is packed using implementation-specific packing instructions. This is true only when the members' data types have no padding bits of their own and when their object representations are the same as their value representations. This frequently is not true for the `_Bool` type or floating-point types and need not be true for pointers. In such cases, the compiler does not insert padding, and use of functions such as `memcmp()` is acceptable.
+
+This compliant example uses the [\#pragma pack](http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx) compiler extension from Microsoft Visual Studio to ensure the structure members are packed as tightly as possible:
+
+```cpp
+#include
+
+#pragma pack(push, 1)
+struct s {
+ char c;
+ int i;
+ char buffer[13];
+};
+#pragma pack(pop)
+
+void compare(const struct s *left, const struct s *right) {
+ if ((left && right) &&
+ (0 == memcmp(left, right, sizeof(struct s)))) {
+ /* ... */
+ }
+}
+```
+
+## Risk Assessment
+
+Comparing padding bytes, when present, can lead to [unexpected program behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP42-C | Medium | Probable | Medium | P8 | L2 |
+
+
+## Automated Detection
+
+
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP42-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP42-C: Do not compare padding data](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.ql b/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.ql
new file mode 100644
index 0000000000..4fb80352a3
--- /dev/null
+++ b/c/cert/src/rules/EXP42-C/DoNotComparePaddingData.ql
@@ -0,0 +1,24 @@
+/**
+ * @id c/cert/do-not-compare-padding-data
+ * @name EXP42-C: Do not compare padding data
+ * @description Padding data values are unspecified and should not be included in comparisons.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/exp42-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.memcmpusedtocomparepaddingdata.MemcmpUsedToComparePaddingData
+
+class DoNotComparePaddingDataQuery extends MemcmpUsedToComparePaddingDataSharedQuery {
+ DoNotComparePaddingDataQuery() { this = Memory2Package::doNotComparePaddingDataQuery() }
+}
diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.md b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.md
new file mode 100644
index 0000000000..855f7ce963
--- /dev/null
+++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.md
@@ -0,0 +1,296 @@
+# EXP43-C: Do not pass aliased pointers to restrict-qualified parameters
+
+This query implements the CERT-C rule EXP43-C:
+
+> Avoid undefined behavior when using restrict-qualified pointers
+
+
+## Description
+
+An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension.
+
+The C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] identifies the following [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+> A restrict-qualified pointer is assigned a value based on another restricted pointer whose associated block neither began execution before the block associated with this pointer, nor ended before the assignment (6.7.3.1).
+
+
+This is an oversimplification, however, and it is important to review the formal definition of *restrict* in subclause 6.7.3.1 of the C Standard to properly understand undefined behaviors associated with the use of `restrict`-qualified pointers.
+
+## Overlapping Objects
+
+The `restrict` qualifier requires that the pointers do not reference overlapping objects. If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is undefined.
+
+**Noncompliant Code Example**
+
+This code example is noncompliant because an assignment is made between two `restrict`-qualified pointers in the same scope:
+
+```cpp
+int *restrict a;
+int *restrict b;
+
+extern int c[];
+
+int main(void) {
+ c[0] = 17;
+ c[1] = 18;
+ a = &c[0];
+ b = &c[1];
+ a = b; /* Undefined behavior */
+ /* ... */
+}
+```
+Note that [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) occurs only when `a` is assigned to `b`. It is valid for `a` and `b` to point into the same array object, provided the range of elements accessed through one of the pointers does not overlap with the range of elements accessed through the other pointer.
+
+**Compliant Solution**
+
+One way to eliminate the [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) is simply to remove the `restrict-`qualification from the affected pointers:
+
+```cpp
+int *a;
+int *b;
+
+extern int c[];
+
+int main(void) {
+ c[0] = 17;
+ c[1] = 18;
+ a = &c[0];
+ b = &c[1];
+ a = b; /* Defined behavior */
+ /* ... */
+}
+```
+
+## restrict-Qualified Function Parameters
+
+When calling functions that have `restrict`-qualified function parameters, it is important that the pointer arguments do not reference overlapping objects if one or more of the pointers are used to modify memory. Consequently, it is important to understand the semantics of the function being called.
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the function `f()` accepts three parameters. The function copies `n` integers from the `int` array referenced by the `restrict`-qualified pointer `p` to the `int` array referenced by the `restrict`-qualified pointer `q`. Because the destination array is modified during each execution of the function (for which `n` is nonzero), if the array is accessed through one of the pointer parameters, it cannot also be accessed through the other. Declaring these function parameters as `restrict`-qualified pointers allows aggressive optimization by the compiler but can also result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if these pointers refer to overlapping objects.
+
+```cpp
+#include
+void f(size_t n, int *restrict p, const int *restrict q) {
+ while (n-- > 0) {
+ *p++ = *q++;
+ }
+}
+
+void g(void) {
+ extern int d[100];
+ /* ... */
+ f(50, d + 1, d); /* Undefined behavior */
+}
+```
+The function `g()` declares an array `d` consisting of 100 `int` values and then invokes `f()` to copy memory from one area of the array to another. This call has undefined behavior because each of `d[1]` through `d[49]` is accessed through both `p` and `q`.
+
+**Compliant Solution**
+
+In this compliant solution, the function `f()` is unchanged but the programmer has ensured that none of the calls to `f()` result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The call to `f()` in `g()` is valid because the storage allocated to `d` is effectively divided into two disjoint objects.
+
+```cpp
+#include
+void f(size_t n, int *restrict p, const int *restrict q) {
+ while (n-- > 0) {
+ *p++ = *q++;
+ }
+}
+
+void g(void) {
+ extern int d[100];
+ /* ... */
+ f(50, d + 50, d); /* Defined behavior */
+}
+```
+**Noncompliant Code Example**
+
+In this noncompliant code example, the function `add()` adds the integer array referenced by the `restrict`-qualified pointers lhs to the integer array referenced by the `restrict`-qualified pointer `rhs` and stores the result in the `restrict`-qualified pointer referenced by `res`. The function `f()` declares an array `a` consisting of 100 `int` values and then invokes `add()` to copy memory from one area of the array to another. The call `add(100, a, a, a)` has [undefined behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior)because the object modified by `res` is accessed by lhs and `rhs`.
+
+```cpp
+#include
+
+void add(size_t n, int *restrict res, const int *restrict lhs,
+ const int *restrict rhs) {
+ for (size_t i = 0; i < n; ++i) {
+ res[i] = lhs[i] + rhs[i];
+ }
+}
+
+void f(void) {
+ int a[100];
+ add(100, a, a, a); /* Undefined behavior */
+}
+```
+**Compliant Solution**
+
+In this compliant solution, an unmodified object is aliased through two restricted pointers. Because `a` and `b` are disjoint arrays, a call of the form `add(100, a, b, b)` has defined behavior, because array `b` is not modified within function `add`.
+
+```cpp
+#include
+void add(size_t n, int *restrict res, const int *restrict lhs,
+ const int *restrict rhs) {
+ for (size_t i = 0; i < n; ++i) {
+ res[i] = lhs[i] + rhs[i];
+ }
+}
+
+void f(void) {
+ int a[100];
+ int b[100];
+ add(100, a, b, b); /* Defined behavior */
+}
+```
+
+## Invoking Library Functions with restrict-Qualified Pointers
+
+Ensure that `restrict`-qualified source and destination pointers do not reference overlapping objects when invoking library functions. For example, the following table lists C standard library functions that copy memory from a source object referenced by a `restrict`-qualified pointer to a destination object that is also referenced by a `restrict`-qualified pointer:
+
+ Standard C | Annex K |
strcpy() | strcpy_s() |
strncpy() | strncpy_s() |
strcat() | strcat_s() |
strncat() | strncat_s() |
memcpy() | memcpy_s() |
| strtok_s() |
+If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 68](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_68).) The result of the functions is unknown, and data may be corrupted. As a result, these functions must never be passed pointers to overlapping objects. If data must be copied between objects that share common memory addresses, a copy function guaranteed to work on overlapping memory, such as `memmove()`, should be used.
+
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the values of objects referenced by `ptr1` and `ptr2` become unpredictable after the call to `memcpy()` because their memory areas overlap:
+
+```cpp
+#include
+
+void func(void) {
+ char c_str[]= "test string";
+ char *ptr1 = c_str;
+ char *ptr2;
+
+ ptr2 = ptr1 + 3;
+ /* Undefined behavior because of overlapping objects */
+ memcpy(ptr2, ptr1, 6);
+ /* ... */
+}
+```
+**Compliant Solution**
+
+In this compliant solution, the call to `memcpy()` is replaced with a call to `memmove()`. The `memmove()` function performs the same operation as `memcpy()` when the memory regions do not overlap. When the memory regions do overlap, the *n* characters from the object pointed to by the source (`ptr1`) are first copied into a temporary array of *n* characters that does not overlap the objects pointed to by the destination (`ptr2`) or the source. The *n* characters from the temporary array are then copied into the object pointed to by the destination.
+
+```cpp
+#include
+
+void func(void) {
+ char c_str[]= "test string";
+ char *ptr1 = c_str;
+ char *ptr2;
+
+ ptr2 = ptr1 + 3;
+ memmove(ptr2, ptr1, 6); /* Replace call to memcpy() */
+ /* ... */
+}
+```
+Similar solutions using `memmove()` can replace the string functions as long as care is taken regarding the byte size of the characters and proper null-termination of the copied string.
+
+## Calling Functions with restrict-Qualified Pointer to a const-Qualified Type
+
+Ensure that functions that accept a `restrict`-qualified pointer to a `const`-qualified type do not modify the object referenced by that pointer. Formatted input and output standard library functions frequently fit this description. The following table lists of some of the common functions for which the format argument is a `restrict`-qualified pointer to a `const`-qualified type.
+
+ Standard C | Annex K |
printf() | printf_s() |
scanf() | scanf_s() |
sprintf() | sprintf_s() |
snprintf() | snprintf_s() |
+For formatted output functions such as `printf()`, it is unlikely that a programmer would modify the format string. However, an attacker may attempt to do so if a program violates [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) and passes tainted values as part of the format string.
+
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the programmer is attempting to overwrite the format string with a string value read in from `stdin` such as `"%d%f 1 3.3"` and use the resulting modified string of `"%s%d%f"` to input the subsequent values of `1` and `3.3`:
+
+```cpp
+#include
+
+void func(void) {
+ int i;
+ float x;
+ char format[100] = "%s";
+ /* Undefined behavior */
+ int n = scanf(format, format + 2, &i, &x);
+ /* ... */
+}
+```
+**Compliant Solution**
+
+The intended results are achieved by this compliant solution:
+
+```cpp
+#include
+
+void func(void) {
+ int i;
+ float x;
+ int n = scanf("%d%f", &i, &x); /* Defined behavior */
+ /* ... */
+}
+```
+
+## Outer-to-Inner Assignments between Restricted Pointers
+
+The assignment between `restrict`-qualified pointers declared in an inner nested block from an outer block has defined behavior.
+
+**Noncompliant Code Example**
+
+The assignment of `restrict`-qualified pointers to other `restrict`-qualified pointers within the same block has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+```cpp
+void func(void) {
+ int *restrict p1;
+ int *restrict q1;
+
+ int *restrict p2 = p1; /* Undefined behavior */
+ int *restrict q2 = q1; /* Undefined behavior */
+ }
+```
+**Compliant Solution**
+
+The intended results can be achieved using an inner nested block, as shown in this compliant solution:
+
+```cpp
+void func(void) {
+ int *restrict p1;
+ int *restrict q1;
+ { /* Added inner block */
+ int *restrict p2 = p1; /* Valid, well-defined behavior */
+ int *restrict q2 = q1; /* Valid, well-defined behavior */
+ }
+}
+```
+
+## Risk Assessment
+
+The incorrect use of `restrict`-qualified pointers can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) that might be [exploited](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) to cause data integrity violations.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP43-C | Medium | Probable | High | P4 | L3 |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP43-C).
+
+## Automated Detection
+
+
+
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions.
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP43-C: Avoid undefined behavior when using restrict-qualified pointers](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql
new file mode 100644
index 0000000000..4aced57136
--- /dev/null
+++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql
@@ -0,0 +1,27 @@
+/**
+ * @id c/cert/do-not-pass-aliased-pointer-to-restrict-qualified-param
+ * @name EXP43-C: Do not pass aliased pointers to restrict-qualified parameters
+ * @description Passing an aliased pointer to a restrict-qualified parameter is undefined behavior.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/exp43-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared
+
+class DoNotPassAliasedPointerToRestrictQualifiedParamQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery
+{
+ DoNotPassAliasedPointerToRestrictQualifiedParamQuery() {
+ this = Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery()
+ }
+}
diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md
new file mode 100644
index 0000000000..ce02712b72
--- /dev/null
+++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md
@@ -0,0 +1,296 @@
+# EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer
+
+This query implements the CERT-C rule EXP43-C:
+
+> Avoid undefined behavior when using restrict-qualified pointers
+
+
+## Description
+
+An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension.
+
+The C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] identifies the following [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+> A restrict-qualified pointer is assigned a value based on another restricted pointer whose associated block neither began execution before the block associated with this pointer, nor ended before the assignment (6.7.3.1).
+
+
+This is an oversimplification, however, and it is important to review the formal definition of *restrict* in subclause 6.7.3.1 of the C Standard to properly understand undefined behaviors associated with the use of `restrict`-qualified pointers.
+
+## Overlapping Objects
+
+The `restrict` qualifier requires that the pointers do not reference overlapping objects. If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is undefined.
+
+**Noncompliant Code Example**
+
+This code example is noncompliant because an assignment is made between two `restrict`-qualified pointers in the same scope:
+
+```cpp
+int *restrict a;
+int *restrict b;
+
+extern int c[];
+
+int main(void) {
+ c[0] = 17;
+ c[1] = 18;
+ a = &c[0];
+ b = &c[1];
+ a = b; /* Undefined behavior */
+ /* ... */
+}
+```
+Note that [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) occurs only when `a` is assigned to `b`. It is valid for `a` and `b` to point into the same array object, provided the range of elements accessed through one of the pointers does not overlap with the range of elements accessed through the other pointer.
+
+**Compliant Solution**
+
+One way to eliminate the [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) is simply to remove the `restrict-`qualification from the affected pointers:
+
+```cpp
+int *a;
+int *b;
+
+extern int c[];
+
+int main(void) {
+ c[0] = 17;
+ c[1] = 18;
+ a = &c[0];
+ b = &c[1];
+ a = b; /* Defined behavior */
+ /* ... */
+}
+```
+
+## restrict-Qualified Function Parameters
+
+When calling functions that have `restrict`-qualified function parameters, it is important that the pointer arguments do not reference overlapping objects if one or more of the pointers are used to modify memory. Consequently, it is important to understand the semantics of the function being called.
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the function `f()` accepts three parameters. The function copies `n` integers from the `int` array referenced by the `restrict`-qualified pointer `p` to the `int` array referenced by the `restrict`-qualified pointer `q`. Because the destination array is modified during each execution of the function (for which `n` is nonzero), if the array is accessed through one of the pointer parameters, it cannot also be accessed through the other. Declaring these function parameters as `restrict`-qualified pointers allows aggressive optimization by the compiler but can also result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if these pointers refer to overlapping objects.
+
+```cpp
+#include
+void f(size_t n, int *restrict p, const int *restrict q) {
+ while (n-- > 0) {
+ *p++ = *q++;
+ }
+}
+
+void g(void) {
+ extern int d[100];
+ /* ... */
+ f(50, d + 1, d); /* Undefined behavior */
+}
+```
+The function `g()` declares an array `d` consisting of 100 `int` values and then invokes `f()` to copy memory from one area of the array to another. This call has undefined behavior because each of `d[1]` through `d[49]` is accessed through both `p` and `q`.
+
+**Compliant Solution**
+
+In this compliant solution, the function `f()` is unchanged but the programmer has ensured that none of the calls to `f()` result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The call to `f()` in `g()` is valid because the storage allocated to `d` is effectively divided into two disjoint objects.
+
+```cpp
+#include
+void f(size_t n, int *restrict p, const int *restrict q) {
+ while (n-- > 0) {
+ *p++ = *q++;
+ }
+}
+
+void g(void) {
+ extern int d[100];
+ /* ... */
+ f(50, d + 50, d); /* Defined behavior */
+}
+```
+**Noncompliant Code Example**
+
+In this noncompliant code example, the function `add()` adds the integer array referenced by the `restrict`-qualified pointers lhs to the integer array referenced by the `restrict`-qualified pointer `rhs` and stores the result in the `restrict`-qualified pointer referenced by `res`. The function `f()` declares an array `a` consisting of 100 `int` values and then invokes `add()` to copy memory from one area of the array to another. The call `add(100, a, a, a)` has [undefined behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior)because the object modified by `res` is accessed by lhs and `rhs`.
+
+```cpp
+#include
+
+void add(size_t n, int *restrict res, const int *restrict lhs,
+ const int *restrict rhs) {
+ for (size_t i = 0; i < n; ++i) {
+ res[i] = lhs[i] + rhs[i];
+ }
+}
+
+void f(void) {
+ int a[100];
+ add(100, a, a, a); /* Undefined behavior */
+}
+```
+**Compliant Solution**
+
+In this compliant solution, an unmodified object is aliased through two restricted pointers. Because `a` and `b` are disjoint arrays, a call of the form `add(100, a, b, b)` has defined behavior, because array `b` is not modified within function `add`.
+
+```cpp
+#include
+void add(size_t n, int *restrict res, const int *restrict lhs,
+ const int *restrict rhs) {
+ for (size_t i = 0; i < n; ++i) {
+ res[i] = lhs[i] + rhs[i];
+ }
+}
+
+void f(void) {
+ int a[100];
+ int b[100];
+ add(100, a, b, b); /* Defined behavior */
+}
+```
+
+## Invoking Library Functions with restrict-Qualified Pointers
+
+Ensure that `restrict`-qualified source and destination pointers do not reference overlapping objects when invoking library functions. For example, the following table lists C standard library functions that copy memory from a source object referenced by a `restrict`-qualified pointer to a destination object that is also referenced by a `restrict`-qualified pointer:
+
+ Standard C | Annex K |
strcpy() | strcpy_s() |
strncpy() | strncpy_s() |
strcat() | strcat_s() |
strncat() | strncat_s() |
memcpy() | memcpy_s() |
| strtok_s() |
+If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 68](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_68).) The result of the functions is unknown, and data may be corrupted. As a result, these functions must never be passed pointers to overlapping objects. If data must be copied between objects that share common memory addresses, a copy function guaranteed to work on overlapping memory, such as `memmove()`, should be used.
+
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the values of objects referenced by `ptr1` and `ptr2` become unpredictable after the call to `memcpy()` because their memory areas overlap:
+
+```cpp
+#include
+
+void func(void) {
+ char c_str[]= "test string";
+ char *ptr1 = c_str;
+ char *ptr2;
+
+ ptr2 = ptr1 + 3;
+ /* Undefined behavior because of overlapping objects */
+ memcpy(ptr2, ptr1, 6);
+ /* ... */
+}
+```
+**Compliant Solution**
+
+In this compliant solution, the call to `memcpy()` is replaced with a call to `memmove()`. The `memmove()` function performs the same operation as `memcpy()` when the memory regions do not overlap. When the memory regions do overlap, the *n* characters from the object pointed to by the source (`ptr1`) are first copied into a temporary array of *n* characters that does not overlap the objects pointed to by the destination (`ptr2`) or the source. The *n* characters from the temporary array are then copied into the object pointed to by the destination.
+
+```cpp
+#include
+
+void func(void) {
+ char c_str[]= "test string";
+ char *ptr1 = c_str;
+ char *ptr2;
+
+ ptr2 = ptr1 + 3;
+ memmove(ptr2, ptr1, 6); /* Replace call to memcpy() */
+ /* ... */
+}
+```
+Similar solutions using `memmove()` can replace the string functions as long as care is taken regarding the byte size of the characters and proper null-termination of the copied string.
+
+## Calling Functions with restrict-Qualified Pointer to a const-Qualified Type
+
+Ensure that functions that accept a `restrict`-qualified pointer to a `const`-qualified type do not modify the object referenced by that pointer. Formatted input and output standard library functions frequently fit this description. The following table lists of some of the common functions for which the format argument is a `restrict`-qualified pointer to a `const`-qualified type.
+
+ Standard C | Annex K |
printf() | printf_s() |
scanf() | scanf_s() |
sprintf() | sprintf_s() |
snprintf() | snprintf_s() |
+For formatted output functions such as `printf()`, it is unlikely that a programmer would modify the format string. However, an attacker may attempt to do so if a program violates [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) and passes tainted values as part of the format string.
+
+
+**Noncompliant Code Example**
+
+In this noncompliant code example, the programmer is attempting to overwrite the format string with a string value read in from `stdin` such as `"%d%f 1 3.3"` and use the resulting modified string of `"%s%d%f"` to input the subsequent values of `1` and `3.3`:
+
+```cpp
+#include
+
+void func(void) {
+ int i;
+ float x;
+ char format[100] = "%s";
+ /* Undefined behavior */
+ int n = scanf(format, format + 2, &i, &x);
+ /* ... */
+}
+```
+**Compliant Solution**
+
+The intended results are achieved by this compliant solution:
+
+```cpp
+#include
+
+void func(void) {
+ int i;
+ float x;
+ int n = scanf("%d%f", &i, &x); /* Defined behavior */
+ /* ... */
+}
+```
+
+## Outer-to-Inner Assignments between Restricted Pointers
+
+The assignment between `restrict`-qualified pointers declared in an inner nested block from an outer block has defined behavior.
+
+**Noncompliant Code Example**
+
+The assignment of `restrict`-qualified pointers to other `restrict`-qualified pointers within the same block has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+```cpp
+void func(void) {
+ int *restrict p1;
+ int *restrict q1;
+
+ int *restrict p2 = p1; /* Undefined behavior */
+ int *restrict q2 = q1; /* Undefined behavior */
+ }
+```
+**Compliant Solution**
+
+The intended results can be achieved using an inner nested block, as shown in this compliant solution:
+
+```cpp
+void func(void) {
+ int *restrict p1;
+ int *restrict q1;
+ { /* Added inner block */
+ int *restrict p2 = p1; /* Valid, well-defined behavior */
+ int *restrict q2 = q1; /* Valid, well-defined behavior */
+ }
+}
+```
+
+## Risk Assessment
+
+The incorrect use of `restrict`-qualified pointers can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) that might be [exploited](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) to cause data integrity violations.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
EXP43-C | Medium | Probable | High | P4 | L3 |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP43-C).
+
+## Automated Detection
+
+
+
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions.
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [EXP43-C: Avoid undefined behavior when using restrict-qualified pointers](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql
new file mode 100644
index 0000000000..31618785d2
--- /dev/null
+++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql
@@ -0,0 +1,90 @@
+/**
+ * @id c/cert/restrict-pointer-references-overlapping-object
+ * @name EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer
+ * @description Restrict qualified pointers referencing overlapping objects is undefined behavior.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/exp43-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p4
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.controlflow.Dominance
+import codingstandards.c.cert
+import codingstandards.cpp.Variable
+
+/**
+ * An `Expr` that is an assignment or initialization to a restrict-qualified pointer-type variable.
+ */
+class AssignmentOrInitializationToRestrictPtrValueExpr extends Expr {
+ Variable v;
+
+ AssignmentOrInitializationToRestrictPtrValueExpr() {
+ this = v.getAnAssignedValue() and
+ v.getType().hasSpecifier("restrict")
+ }
+
+ Variable getVariable() { result = v }
+
+ predicate isTargetRestrictQualifiedAndInSameScope() {
+ this.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and
+ this.(VariableAccess).getTarget().getParentScope() = this.getVariable().getParentScope()
+ }
+}
+
+/**
+ * A data-flow configuration for tracking flow from an assignment or initialization to
+ * an assignment to an `AssignmentOrInitializationToRestrictPtrValueExpr`.
+ */
+module AssignedValueToRestrictPtrValueConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ exists(Variable v | source.asExpr() = v.getAnAssignedValue())
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() instanceof AssignmentOrInitializationToRestrictPtrValueExpr
+ }
+}
+
+module AssignedValueToRestrictPtrValueFlow =
+ DataFlow::Global;
+
+from
+ AssignmentOrInitializationToRestrictPtrValueExpr expr, DataFlow::Node sourceValue,
+ string sourceMessage
+where
+ not isExcluded(expr, Pointers3Package::restrictPointerReferencesOverlappingObjectQuery()) and
+ (
+ // Two restrict-qualified pointers in the same scope assigned to each other
+ expr.isTargetRestrictQualifiedAndInSameScope() and
+ sourceValue.asExpr() = expr and
+ sourceMessage = "the object pointed to by " + expr.(VariableAccess).getTarget().getName()
+ or
+ // If the same expressions flows to two unique `AssignmentOrInitializationToRestrictPtrValueExpr`
+ // in the same block, then the two variables point to the same (overlapping) object
+ not expr.isTargetRestrictQualifiedAndInSameScope() and
+ exists(AssignmentOrInitializationToRestrictPtrValueExpr pre_expr |
+ expr.getEnclosingBlock() = pre_expr.getEnclosingBlock() and
+ (
+ AssignedValueToRestrictPtrValueFlow::flow(sourceValue, DataFlow::exprNode(pre_expr)) and
+ AssignedValueToRestrictPtrValueFlow::flow(sourceValue, DataFlow::exprNode(expr)) and
+ sourceMessage = "the same source value"
+ or
+ // Expressions referring to the address of the same variable can also result in aliasing
+ getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) and
+ sourceValue.asExpr() = pre_expr and
+ sourceMessage = getAddressOfExprTargetBase(expr).getName() + " via address-of"
+ ) and
+ strictlyDominates(pragma[only_bind_out](pre_expr), pragma[only_bind_out](expr))
+ )
+ )
+select expr, "Assignment to restrict-qualified pointer $@ results in pointers aliasing $@.",
+ expr.getVariable(), expr.getVariable().getName(), sourceValue, sourceMessage
diff --git a/c/cert/src/rules/EXP44-C/UnevaluatedOperandWithSideEffect.ql b/c/cert/src/rules/EXP44-C/UnevaluatedOperandWithSideEffect.ql
index 32d30a09ad..02d71b3497 100644
--- a/c/cert/src/rules/EXP44-C/UnevaluatedOperandWithSideEffect.ql
+++ b/c/cert/src/rules/EXP44-C/UnevaluatedOperandWithSideEffect.ql
@@ -9,6 +9,11 @@
* @problem.severity error
* @tags external/cert/id/exp44-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/EXP45-C/AssignmentsInSelectionStatements.ql b/c/cert/src/rules/EXP45-C/AssignmentsInSelectionStatements.ql
index f6e29eb28c..c831713486 100644
--- a/c/cert/src/rules/EXP45-C/AssignmentsInSelectionStatements.ql
+++ b/c/cert/src/rules/EXP45-C/AssignmentsInSelectionStatements.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/exp45-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql
index 040a8bb6ee..549e57236a 100644
--- a/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql
+++ b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/exp46-c
* maintainability
* readability
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p9
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO30-C/ExcludeUserInputFromFormatStrings.ql b/c/cert/src/rules/FIO30-C/ExcludeUserInputFromFormatStrings.ql
index b9df838b06..81ecf56ccf 100644
--- a/c/cert/src/rules/FIO30-C/ExcludeUserInputFromFormatStrings.ql
+++ b/c/cert/src/rules/FIO30-C/ExcludeUserInputFromFormatStrings.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio30-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p18
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql b/c/cert/src/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql
index 2d16b2ffea..78817d31e9 100644
--- a/c/cert/src/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql
+++ b/c/cert/src/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql
@@ -8,15 +8,21 @@
* @tags external/cert/id/fio32-c
* correctness
* security
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import semmle.code.cpp.security.FunctionWithWrappers
-import semmle.code.cpp.security.Security
-import semmle.code.cpp.security.TaintTracking
-import TaintedWithPath
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.ir.IR
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import TaintedPath::PathGraph
// Query TaintedPath.ql from the CodeQL standard library
/**
@@ -45,20 +51,60 @@ class FileFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { arg = 0 }
}
-class TaintedPathConfiguration extends TaintTrackingConfiguration {
- override predicate isSink(Element tainted) {
- exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
+/**
+ * Holds for a variable that has any kind of upper-bound check anywhere in the program.
+ * This is biased towards being inclusive and being a coarse overapproximation because
+ * there are a lot of valid ways of doing an upper bounds checks if we don't consider
+ * where it occurs, for example:
+ * ```cpp
+ * if (x < 10) { sink(x); }
+ *
+ * if (10 > y) { sink(y); }
+ *
+ * if (z > 10) { z = 10; }
+ * sink(z);
+ * ```
+ */
+predicate hasUpperBoundsCheck(Variable var) {
+ exists(RelationalOperation oper, VariableAccess access |
+ oper.getAnOperand() = access and
+ access.getTarget() = var and
+ // Comparing to 0 is not an upper bound check
+ not oper.getAnOperand().getValue() = "0"
+ )
+}
+
+module TaintedPathConfiguration implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
+
+ predicate isSink(DataFlow::Node node) {
+ exists(FileFunction fileFunction |
+ fileFunction.outermostWrapperFunctionCall(node.asIndirectArgument(), _)
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node) {
+ node.asExpr().(Call).getTarget().getUnspecifiedType() instanceof ArithmeticType
+ or
+ exists(LoadInstruction load, Variable checkedVar |
+ load = node.asInstruction() and
+ checkedVar = load.getSourceAddress().(VariableAddressInstruction).getAstVariable() and
+ hasUpperBoundsCheck(checkedVar)
+ )
}
}
+module TaintedPath = TaintTracking::Global;
+
from
- FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
- PathNode sinkNode, string taintCause, string callChain
+ FileFunction fileFunction, Expr taintedArg, FlowSource taintSource,
+ TaintedPath::PathNode sourceNode, TaintedPath::PathNode sinkNode, string callChain
where
not isExcluded(taintedArg, IO3Package::doNotPerformFileOperationsOnDevicesQuery()) and
+ taintedArg = sinkNode.getNode().asIndirectArgument() and
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
- taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
- isUserInput(taintSource, taintCause)
+ TaintedPath::flowPath(sourceNode, sinkNode) and
+ taintSource = sourceNode.getNode()
select taintedArg, sourceNode, sinkNode,
- "This argument to a file access function is derived from $@ and then passed to " + callChain,
- taintSource, "user input (" + taintCause + ")"
+ "This argument to a file access function is derived from $@ and then passed to " + callChain + ".",
+ taintSource, "user input (" + taintSource.getSourceType() + ")"
diff --git a/c/cert/src/rules/FIO34-C/DistinguishBetweenCharReadFromAFileAndEofOrWeof.ql b/c/cert/src/rules/FIO34-C/DistinguishBetweenCharReadFromAFileAndEofOrWeof.ql
index a55c2dbf29..01c13e642b 100644
--- a/c/cert/src/rules/FIO34-C/DistinguishBetweenCharReadFromAFileAndEofOrWeof.ql
+++ b/c/cert/src/rules/FIO34-C/DistinguishBetweenCharReadFromAFileAndEofOrWeof.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio34-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO34-C/EndOfFileCheckPortability.ql b/c/cert/src/rules/FIO34-C/EndOfFileCheckPortability.ql
index 274514e598..3336a059cd 100644
--- a/c/cert/src/rules/FIO34-C/EndOfFileCheckPortability.ql
+++ b/c/cert/src/rules/FIO34-C/EndOfFileCheckPortability.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/fio34-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO37-C/SuccessfulFgetsOrFgetwsMayReturnAnEmptyString.ql b/c/cert/src/rules/FIO37-C/SuccessfulFgetsOrFgetwsMayReturnAnEmptyString.ql
index 54f555d7cb..ad3a2c8192 100644
--- a/c/cert/src/rules/FIO37-C/SuccessfulFgetsOrFgetwsMayReturnAnEmptyString.ql
+++ b/c/cert/src/rules/FIO37-C/SuccessfulFgetsOrFgetwsMayReturnAnEmptyString.ql
@@ -7,6 +7,11 @@
* @problem.severity error
* @tags external/cert/id/fio37-c
* correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p12
+ * external/cert/level/l1
* external/cert/obligation/rule
*/
@@ -14,7 +19,7 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.FgetsErrorManagement
import codingstandards.cpp.Dereferenced
-import semmle.code.cpp.dataflow.TaintTracking
+import semmle.code.cpp.dataflow.DataFlow
/*
* CFG nodes that follows a successful call to `fgets`
diff --git a/c/cert/src/rules/FIO38-C/DoNotCopyAFileObject.ql b/c/cert/src/rules/FIO38-C/DoNotCopyAFileObject.ql
index e8e897009e..5b5a043395 100644
--- a/c/cert/src/rules/FIO38-C/DoNotCopyAFileObject.ql
+++ b/c/cert/src/rules/FIO38-C/DoNotCopyAFileObject.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio38-c
* correctness
* security
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql b/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql
index 4983e3a69a..09289d1f79 100644
--- a/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql
+++ b/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/fio39-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
@@ -15,7 +20,8 @@ import cpp
import codingstandards.c.cert
import codingstandards.cpp.rules.iofstreammissingpositioning.IOFstreamMissingPositioning
-class DoNotAlternatelyIOFromAStreamWithoutPositioningQuery extends IOFstreamMissingPositioningSharedQuery {
+class DoNotAlternatelyIOFromAStreamWithoutPositioningQuery extends IOFstreamMissingPositioningSharedQuery
+{
DoNotAlternatelyIOFromAStreamWithoutPositioningQuery() {
this = IO1Package::doNotAlternatelyIOFromAStreamWithoutPositioningQuery()
}
diff --git a/c/cert/src/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.ql b/c/cert/src/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.ql
index 69fb92a15c..9b0882ac66 100644
--- a/c/cert/src/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.ql
+++ b/c/cert/src/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.ql
@@ -9,6 +9,11 @@
* @tags external/cert/id/fio40-c
* correctness
* security
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -16,6 +21,7 @@ import cpp
import codingstandards.cpp.FgetsErrorManagement
import codingstandards.cpp.Dereferenced
import codingstandards.c.cert
+import semmle.code.cpp.dataflow.DataFlow
/*
* Models calls to `memcpy` `strcpy` `strncpy` and their wrappers
diff --git a/c/cert/src/rules/FIO41-C/DoNotCallGetcAndPutcWithSideEffects.ql b/c/cert/src/rules/FIO41-C/DoNotCallGetcAndPutcWithSideEffects.ql
index 7fc1c11d26..5c7d759606 100644
--- a/c/cert/src/rules/FIO41-C/DoNotCallGetcAndPutcWithSideEffects.ql
+++ b/c/cert/src/rules/FIO41-C/DoNotCallGetcAndPutcWithSideEffects.ql
@@ -8,6 +8,11 @@
* @problem.severity error
* @tags external/cert/id/fio41-c
* correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.md b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.md
index 91654e8ee2..f84163ae4a 100644
--- a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.md
+++ b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.md
@@ -180,7 +180,7 @@ Failing to properly close files may allow an attacker to exhaust system resource
This rule is stricter than rule \[fileclose\] in [ISO/IEC TS 17961:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IECTS17961). Analyzers that conform to the technical standard may not detect all violations of this rule.
-
+
## Related Vulnerabilities
diff --git a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql
index 2c7959f38e..26f8aa239d 100644
--- a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql
+++ b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql
@@ -9,166 +9,21 @@
* @tags external/cert/id/fio42-c
* correctness
* security
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
-import semmle.code.cpp.controlflow.StackVariableReachability
-import codingstandards.cpp.standardlibrary.FileAccess
+import codingstandards.cpp.rules.closefilehandlewhennolongerneededshared.CloseFileHandleWhenNoLongerNeededShared
-/**
- * Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value
- * (for example 'open' returns -1 if there was an error)
- */
-class MinusOne extends NullValue {
- MinusOne() { this.(UnaryMinusExpr).getOperand().(Literal).getValue() = "1" }
-}
-
-/**
- * 'call' is either a direct call to f, or a possible call to f
- * via a function pointer.
- */
-predicate mayCallFunction(Expr call, Function f) {
- call.(FunctionCall).getTarget() = f or
- call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() =
- f
-}
-
-predicate fopenCallOrIndirect(Expr e) {
- // direct fopen call
- opened(e) and
- // We are only interested in fopen calls that are
- // actually closed somehow, as FileNeverClosed
- // will catch those that aren't.
- fopenCallMayBeClosed(e)
- or
- exists(ReturnStmt rtn |
- // indirect fopen call
- mayCallFunction(e, rtn.getEnclosingFunction()) and
- (
- // return fopen
- fopenCallOrIndirect(rtn.getExpr())
- or
- // return variable assigned with fopen
- exists(Variable v |
- v = rtn.getExpr().(VariableAccess).getTarget() and
- fopenCallOrIndirect(v.getAnAssignedValue()) and
- not assignedToFieldOrGlobal(v, _)
- )
- )
- )
-}
-
-predicate fcloseCallOrIndirect(FunctionCall fc, Variable v) {
- // direct fclose call
- fcloseCall(fc, v.getAnAccess())
- or
- // indirect fclose call
- exists(FunctionCall midcall, Function mid, int arg |
- fc.getArgument(arg) = v.getAnAccess() and
- mayCallFunction(fc, mid) and
- midcall.getEnclosingFunction() = mid and
- fcloseCallOrIndirect(midcall, mid.getParameter(arg))
- )
-}
-
-predicate fopenDefinition(StackVariable v, ControlFlowNode def) {
- exists(Expr expr | exprDefinition(v, def, expr) and fopenCallOrIndirect(expr))
-}
-
-class FOpenVariableReachability extends StackVariableReachabilityWithReassignment {
- FOpenVariableReachability() { this = "FOpenVariableReachability" }
-
- override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
- fopenDefinition(v, node)
- }
-
- override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
- // node may be used in fopenReaches
- exists(node.(AnalysedExpr).getNullSuccessor(v)) or
- fcloseCallOrIndirect(node, v) or
- assignedToFieldOrGlobal(v, node) or
- // node may be used directly in query
- v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
- }
-
- override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
-}
-
-/**
- * The value from fopen at `def` is still held in Variable `v` upon entering `node`.
- */
-predicate fopenVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
- exists(FOpenVariableReachability r |
- // reachability
- r.reachesTo(def, _, node, v)
- or
- // accept def node itself
- r.isSource(def, v) and
- node = def
- )
-}
-
-class FOpenReachability extends StackVariableReachabilityExt {
- FOpenReachability() { this = "FOpenReachability" }
-
- override predicate isSource(ControlFlowNode node, StackVariable v) { fopenDefinition(v, node) }
-
- override predicate isSink(ControlFlowNode node, StackVariable v) {
- v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
- }
-
- override predicate isBarrier(
- ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
- ) {
- isSource(source, v) and
- next = node.getASuccessor() and
- // the file (stored in any variable `v0`) opened at `source` is closed or
- // assigned to a global at node, or NULL checked on the edge node -> next.
- exists(StackVariable v0 | fopenVariableReaches(v0, source, node) |
- node.(AnalysedExpr).getNullSuccessor(v0) = next or
- fcloseCallOrIndirect(node, v0) or
- assignedToFieldOrGlobal(v0, node)
- )
+class CloseFilesWhenTheyAreNoLongerNeededQuery extends CloseFileHandleWhenNoLongerNeededSharedSharedQuery
+{
+ CloseFilesWhenTheyAreNoLongerNeededQuery() {
+ this = IO1Package::closeFilesWhenTheyAreNoLongerNeededQuery()
}
}
-
-/**
- * The value returned by fopen `def` has not been closed, confirmed to be null,
- * or potentially leaked globally upon reaching `node` (regardless of what variable
- * it's still held in, if any).
- */
-predicate fopenReaches(ControlFlowNode def, ControlFlowNode node) {
- exists(FOpenReachability r | r.reaches(def, _, node))
-}
-
-predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
- // assigned to anything except a StackVariable
- // (typically a field or global, but for example also *ptr = v)
- e.(Assignment).getRValue() = v.getAnAccess() and
- not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
- or
- exists(Expr midExpr, Function mid, int arg |
- // indirect assignment
- e.(FunctionCall).getArgument(arg) = v.getAnAccess() and
- mayCallFunction(e, mid) and
- midExpr.getEnclosingFunction() = mid and
- assignedToFieldOrGlobal(mid.getParameter(arg), midExpr)
- )
- or
- // assigned to a field via constructor field initializer
- e.(ConstructorFieldInit).getExpr() = v.getAnAccess()
-}
-
-from ControlFlowNode def, Stmt ret
-where
- not isExcluded(def, IO1Package::closeFilesWhenTheyAreNoLongerNeededQuery()) and
- fopenReaches(def, ret) and
- not exists(StackVariable v |
- fopenVariableReaches(v, def, ret) and
- ret.getAChild*() = v.getAnAccess()
- )
- or
- opened(def) and not fopenCallMayBeClosed(def) and ret = def.getControlFlowScope().getEntryPoint()
-select def, "The file opened here may not be closed at $@.", ret, "this location"
diff --git a/c/cert/src/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql b/c/cert/src/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql
index 9d5058507e..bc0a417bd0 100644
--- a/c/cert/src/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql
+++ b/c/cert/src/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql
@@ -7,6 +7,11 @@
* @problem.severity error
* @tags external/cert/id/fio44-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
@@ -22,24 +27,24 @@ class FsetposCall extends FunctionCall {
FsetposCall() { this.getTarget().hasGlobalOrStdName("fsetpos") }
}
-class FposDFConf extends DataFlow::Configuration {
- FposDFConf() { this = "FposDFConf" }
-
- override predicate isSource(DataFlow::Node source) {
+module FposDFConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
// source must be the second parameter of a FgetposCall call
source = DataFlow::definitionByReferenceNodeFromArgument(any(FgetposCall c).getArgument(1))
}
- override predicate isSink(DataFlow::Node sink) {
+ predicate isSink(DataFlow::Node sink) {
// sink must be the second parameter of a FsetposCall call
sink.asExpr() = any(FsetposCall c).getArgument(1)
}
}
+module FposDFFlow = DataFlow::Global;
+
from FsetposCall fsetpos
where
not isExcluded(fsetpos.getArgument(1),
IO2Package::onlyUseValuesForFsetposThatAreReturnedFromFgetposQuery()) and
- not any(FposDFConf dfConf).hasFlowToExpr(fsetpos.getArgument(1))
+ not FposDFFlow::flowToExpr(fsetpos.getArgument(1))
select fsetpos.getArgument(1),
"The position argument of a call to `fsetpos()` should be obtained from a call to `fgetpos()`."
diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql
index b02ce2f58d..85369b502e 100644
--- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql
+++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio45-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql b/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql
index 6bc284c2c7..dc52dca487 100644
--- a/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql
+++ b/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql
@@ -7,6 +7,11 @@
* @problem.severity error
* @tags external/cert/id/fio46-c
* correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p4
+ * external/cert/level/l3
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql
index 2062cba2c4..8ed99d4541 100644
--- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql
+++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio47-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql
index a8b9e9fbac..7266f1fc7c 100644
--- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql
+++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio47-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql
index 66cbe409f6..00853abfbc 100644
--- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql
+++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql
@@ -8,6 +8,11 @@
* @tags external/cert/id/fio47-c
* correctness
* security
+ * external/cert/severity/high
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
* external/cert/obligation/rule
*/
diff --git a/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md
new file mode 100644
index 0000000000..52e2c70fce
--- /dev/null
+++ b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md
@@ -0,0 +1,106 @@
+# FLP30-C: Do not use floating-point variables as loop counters
+
+This query implements the CERT-C rule FLP30-C:
+
+> Do not use floating-point variables as loop counters
+
+
+## Description
+
+Because floating-point numbers represent real numbers, it is often mistakenly assumed that they can represent any simple fraction exactly. Floating-point numbers are subject to representational limitations just as integers are, and binary floating-point numbers cannot represent all real numbers exactly, even if they can be represented in a small number of decimal digits.
+
+In addition, because floating-point numbers can represent large values, it is often mistakenly assumed that they can represent all significant digits of those values. To gain a large dynamic range, floating-point numbers maintain a fixed number of precision bits (also called the significand) and an exponent, which limit the number of significant digits they can represent.
+
+Different implementations have different precision limitations, and to keep code portable, floating-point variables must not be used as the loop induction variable. See Goldberg's work for an introduction to this topic \[[Goldberg 1991](https://www.securecoding.cert.org/confluence/display/java/Rule+AA.+References#RuleAA.References-Goldberg91)\].
+
+For the purpose of this rule, a *loop counter* is an induction variable that is used as an operand of a comparison expression that is used as the controlling expression of a `do`, `while`, or `for` loop. An *induction variable* is a variable that gets increased or decreased by a fixed amount on every iteration of a loop \[[Aho 1986](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Aho1986)\]. Furthermore, the change to the variable must occur directly in the loop body (rather than inside a function executed within the loop).
+
+## Noncompliant Code Example
+
+In this noncompliant code example, a floating-point variable is used as a loop counter. The decimal number `0.1` is a repeating fraction in binary and cannot be exactly represented as a binary floating-point number. Depending on the implementation, the loop may iterate 9 or 10 times.
+
+```cpp
+void func(void) {
+ for (float x = 0.1f; x <= 1.0f; x += 0.1f) {
+ /* Loop may iterate 9 or 10 times */
+ }
+}
+```
+For example, when compiled with GCC or Microsoft Visual Studio 2013 and executed on an x86 processor, the loop is evaluated only nine times.
+
+## Compliant Solution
+
+In this compliant solution, the loop counter is an integer from which the floating-point value is derived:
+
+```cpp
+#include
+
+void func(void) {
+ for (size_t count = 1; count <= 10; ++count) {
+ float x = count / 10.0f;
+ /* Loop iterates exactly 10 times */
+ }
+}
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, a floating-point loop counter is incremented by an amount that is too small to change its value given its precision:
+
+```cpp
+void func(void) {
+ for (float x = 100000001.0f; x <= 100000010.0f; x += 1.0f) {
+ /* Loop may not terminate */
+ }
+}
+```
+On many implementations, this produces an infinite loop.
+
+## Compliant Solution
+
+In this compliant solution, the loop counter is an integer from which the floating-point value is derived. The variable `x` is assigned a computed value to reduce compounded rounding errors that are present in the noncompliant code example.
+
+```cpp
+void func(void) {
+ for (size_t count = 1; count <= 10; ++count) {
+ float x = 100000000.0f + (count * 1.0f);
+ /* Loop iterates exactly 10 times */
+ }
+}
+```
+
+## Risk Assessment
+
+The use of floating-point variables as loop counters can result in [unexpected behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
FLP30-C | Low | Probable | Low | P6 | L2 |
+
+
+## Automated Detection
+
+
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP30-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [FLP30-C: Do not use floating-point variables as loop counters](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql
new file mode 100644
index 0000000000..a042d80ba5
--- /dev/null
+++ b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql
@@ -0,0 +1,56 @@
+/**
+ * @id c/cert/floating-point-loop-counters
+ * @name FLP30-C: Do not use floating-point variables as loop counters
+ * @description Loop counters should not use floating-point variables to keep code portable.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity recommendation
+ * @tags external/cert/id/flp30-c
+ * maintainability
+ * readability
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.Loops
+
+/*
+ * A variable that is increased or decreased by a fixed amount on each iteration.
+ */
+
+class InductionVariable extends Variable {
+ Loop loop;
+ Expr update;
+
+ InductionVariable() {
+ update.getParent+() = loop and
+ (
+ update.(AssignArithmeticOperation).getRValue().isConstant() and
+ update.(AssignArithmeticOperation).getLValue() = this.getAnAccess()
+ or
+ exists(BinaryArithmeticOperation binop |
+ update.(Assignment).getLValue() = this.getAnAccess() and
+ update.(Assignment).getRValue() = binop and
+ binop.getAnOperand() = this.getAnAccess() and
+ binop.getAnOperand().isConstant()
+ )
+ or
+ update.(CrementOperation).getOperand() = this.getAnAccess()
+ )
+ }
+}
+
+from Loop loop, InductionVariable loopCounter, ComparisonOperation comparison
+where
+ not isExcluded(loop, Statements4Package::floatingPointLoopCountersQuery()) and
+ loop.getControllingExpr() = comparison and
+ comparison.getAnOperand() = loopCounter.getAnAccess() and
+ loopCounter.getType() instanceof FloatingPointType
+select loop, "Loop using a $@ of type floating-point.", loopCounter, "loop counter"
diff --git a/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.md b/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.md
new file mode 100644
index 0000000000..ca24a02498
--- /dev/null
+++ b/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.md
@@ -0,0 +1,352 @@
+# FLP32-C: Prevent or detect domain and range errors in math functions
+
+This query implements the CERT-C rule FLP32-C:
+
+> Prevent or detect domain and range errors in math functions
+
+
+## Description
+
+The C Standard, 7.12.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines three types of errors that relate specifically to math functions in ``. Paragraph 2 states
+
+> *A domain error* occurs if an input argument is outside the domain over which the mathematical function is defined.
+
+
+Paragraph 3 states
+
+> A *pole error* (also known as a singularity or infinitary) occurs if the mathematical function has an exact infinite result as the finite input argument(s) are approached in the limit.
+
+
+Paragraph 4 states
+
+> A *range error* occurs if the mathematical result of the function cannot be represented in an object of the specified type, due to extreme magnitude.
+
+
+An example of a domain error is the square root of a negative number, such as `sqrt(-1.0)`, which has no meaning in real arithmetic. Contrastingly, 10 raised to the 1-millionth power, `pow(10., 1e6)`, cannot be represented in many floating-point [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) because of the limited range of the type `double` and consequently constitutes a range error. In both cases, the function will return some value, but the value returned is not the correct result of the computation. An example of a pole error is `log(0.0)`, which results in negative infinity.
+
+Programmers can prevent domain and pole errors by carefully bounds-checking the arguments before calling mathematical functions and taking alternative action if the bounds are violated.
+
+Range errors usually cannot be prevented because they are dependent on the implementation of floating-point numbers as well as on the function being applied. Instead of preventing range errors, programmers should attempt to detect them and take alternative action if a range error occurs.
+
+The following table lists the `double` forms of standard mathematical functions, along with checks that should be performed to ensure a proper input domain, and indicates whether they can also result in range or pole errors, as reported by the C Standard. Both `float` and `long double` forms of these functions also exist but are omitted from the table for brevity. If a function has a specific domain over which it is defined, the programmer must check its input values. The programmer must also check for range errors where they might occur. The standard math functions not listed in this table, such as `fabs()`, have no domain restrictions and cannot result in range or pole errors.
+
+ Function | Domain | Range | Pole |
acos(x) | -1 <= x && x <= 1 | No | No |
asin(x) | -1 <= x && x <= 1 | Yes | No |
atan(x) | None | Yes | No |
atan2(y, x) | None | No | No |
acosh(x) | x >= 1 | Yes | No |
asinh(x) | None | Yes | No |
atanh(x) | -1 < x && x < 1 | Yes | Yes |
cosh(x) , sinh(x) | None | Yes | No |
exp(x) , exp2(x) , expm1(x) | None | Yes | No |
ldexp(x, exp) | None | Yes | No |
log(x) , log10(x) , log2(x) | x >= 0 | No | Yes |
log1p(x) | x >= -1 | No | Yes |
ilogb(x) | x != 0 && !isinf(x) && !isnan(x) | Yes | No |
logb(x) | x != 0 | Yes | Yes |
scalbn(x, n) , scalbln(x, n) | None | Yes | No |
hypot(x, y) | None | Yes | No |
pow(x,y) | x > 0 || (x == 0 && y > 0) || ( x < 0 && y is an integer) | Yes | Yes |
sqrt(x) | x >= 0 | No | No |
erf(x) | None | Yes | No |
erfc(x) | None | Yes | No |
lgamma(x) , tgamma(x) | x != 0 && ! ( x < 0 && x is an integer) | Yes | Yes |
lrint(x) , lround(x) | None | Yes | No |
fmod(x, y) , remainder(x, y) , remquo(x, y, quo) | y != 0 | Yes | No |
nextafter(x, y) , nexttoward(x, y) | None | Yes | No |
fdim(x,y) | None | Yes | No |
fma(x,y,z) | None | Yes | No |
+
+
+## Domain and Pole Checking
+
+The most reliable way to handle domain and pole errors is to prevent them by checking arguments beforehand, as in the following exemplar:
+
+```cpp
+double safe_sqrt(double x) {
+ if (x < 0) {
+ fprintf(stderr, "sqrt requires a nonnegative argument");
+ /* Handle domain / pole error */
+ }
+ return sqrt (x);
+}
+
+```
+
+## Range Checking
+
+Programmers usually cannot prevent range errors, so the most reliable way to handle them is to detect when they have occurred and act accordingly.
+
+The exact treatment of error conditions from math functions is tedious. The C Standard, 7.12.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines the following behavior for floating-point overflow:
+
+> A floating result overflows if the magnitude of the mathematical result is finite but so large that the mathematical result cannot be represented without extraordinary roundoff error in an object of the specified type. If a floating result overflows and default rounding is in effect, then the function returns the value of the macro `HUGE_VAL`, `HUGE_VALF`, or `HUGE_VALL` according to the return type, with the same sign as the correct value of the function; if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, the integer expression `errno` acquires the value `ERANGE`; if the integer expression `math_errhandling & MATH_ERREXCEPT` is nonzero, the "overflow" floating-point exception is raised.
+
+
+It is preferable not to check for errors by comparing the returned value against `HUGE_VAL` or `0` for several reasons:
+
+* These are, in general, valid (albeit unlikely) data values.
+* Making such tests requires detailed knowledge of the various error returns for each math function.
+* Multiple results aside from `HUGE_VAL` and `0` are possible, and programmers must know which are possible in each case.
+* Different versions of the library have varied in their error-return behavior.
+It can be unreliable to check for math errors using `errno` because an [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) might not set `errno`. For real functions, the programmer determines if the implementation sets `errno` by checking whether `math_errhandling & MATH_ERRNO` is nonzero. For complex functions, the C Standard, 7.3.2, paragraph 1, simply states that "an implementation may set `errno` but is not required to" \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\].
+
+The obsolete *System V Interface Definition* (SVID3) \[[UNIX 1992](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-UNIX92)\] provides more control over the treatment of errors in the math library. The programmer can define a function named `matherr()` that is invoked if errors occur in a math function. This function can print diagnostics, terminate the execution, or specify the desired return value. The `matherr()` function has not been adopted by C or POSIX, so it is not generally portable.
+
+The following error-handing template uses C Standard functions for floating-point errors when the C macro `math_errhandling` is defined and indicates that they should be used; otherwise, it examines `errno`:
+
+```cpp
+#include
+#include
+#include
+
+/* ... */
+/* Use to call a math function and check errors */
+{
+ #pragma STDC FENV_ACCESS ON
+
+ if (math_errhandling & MATH_ERREXCEPT) {
+ feclearexcept(FE_ALL_EXCEPT);
+ }
+ errno = 0;
+
+ /* Call the math function */
+
+ if ((math_errhandling & MATH_ERRNO) && errno != 0) {
+ /* Handle range error */
+ } else if ((math_errhandling & MATH_ERREXCEPT) &&
+ fetestexcept(FE_INVALID | FE_DIVBYZERO |
+ FE_OVERFLOW | FE_UNDERFLOW) != 0) {
+ /* Handle range error */
+ }
+}
+
+```
+See [FLP03-C. Detect and handle floating-point errors](https://wiki.sei.cmu.edu/confluence/display/c/FLP03-C.+Detect+and+handle+floating-point+errors) for more details on how to detect floating-point errors.
+
+## Subnormal Numbers
+
+A subnormal number is a nonzero number that does not use all of its precision bits \[[IEEE 754 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEE7542006)\]. These numbers can be used to represent values that are closer to 0 than the smallest normal number (one that uses all of its precision bits). However, the `asin()`, `asinh()`, `atan()`, `atanh()`, and `erf()` functions may produce range errors, specifically when passed a subnormal number. When evaluated with a subnormal number, these functions can produce an inexact, subnormal value, which is an underflow error. The C Standard, 7.12.1, paragraph 6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines the following behavior for floating-point underflow:
+
+> The result underflows if the magnitude of the mathematical result is so small that the mathematical result cannot be represented, without extraordinary roundoff error, in an object of the specified type. If the result underflows, the function returns an implementation-defined value whose magnitude is no greater than the smallest normalized positive number in the specified type; if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, whether `errno ` acquires the value `ERANGE ` is implementation-defined; if the integer expression `math_errhandling & MATH_ERREXCEPT` is nonzero, whether the ‘‘underflow’’ floating-point exception is raised is implementation-defined.
+
+
+Implementations that support floating-point arithmetic but do not support subnormal numbers, such as IBM S/360 hex floating-point or nonconforming IEEE-754 implementations that skip subnormals (or support them by flushing them to zero), can return a range error when calling one of the following families of functions with the following arguments:
+
+* `fmod`((min+subnorm), min)``
+* `remainder`((min+`subnorm`), min)``
+* `remquo`((min+`subnorm`), min, quo)``
+where `min` is the minimum value for the corresponding floating point type and `subnorm` is a subnormal value.
+
+If Annex F is supported and subnormal results are supported, the returned value is exact and a range error cannot occur. The C Standard, F.10.7.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], specifies the following for the `fmod()`, `remainder()`, and `remquo()` functions:
+
+> When subnormal results are supported, the returned value is exact and is independent of the current rounding direction mode.
+
+
+Annex F, subclause F.10.7.2, paragraph 2, and subclause F.10.7.3, paragraph 2, of the C Standard identify when subnormal results are supported.
+
+## Noncompliant Code Example (sqrt())
+
+This noncompliant code example determines the square root of `x`:
+
+```cpp
+#include
+
+void func(double x) {
+ double result;
+ result = sqrt(x);
+}
+```
+However, this code may produce a domain error if `x` is negative.
+
+## Compliant Solution (sqrt())
+
+Because this function has domain errors but no range errors, bounds checking can be used to prevent domain errors:
+
+```cpp
+#include
+
+void func(double x) {
+ double result;
+
+ if (isless(x, 0.0)) {
+ /* Handle domain error */
+ }
+
+ result = sqrt(x);
+}
+```
+
+## Noncompliant Code Example (sinh(), Range Errors)
+
+This noncompliant code example determines the hyperbolic sine of `x`:
+
+```cpp
+#include
+
+void func(double x) {
+ double result;
+ result = sinh(x);
+}
+```
+This code may produce a range error if `x` has a very large magnitude.
+
+## Compliant Solution (sinh(), Range Errors)
+
+Because this function has no domain errors but may have range errors, the programmer must detect a range error and act accordingly:
+
+```cpp
+#include
+#include
+#include
+
+void func(double x) {
+ double result;
+ {
+ #pragma STDC FENV_ACCESS ON
+ if (math_errhandling & MATH_ERREXCEPT) {
+ feclearexcept(FE_ALL_EXCEPT);
+ }
+ errno = 0;
+
+ result = sinh(x);
+
+ if ((math_errhandling & MATH_ERRNO) && errno != 0) {
+ /* Handle range error */
+ } else if ((math_errhandling & MATH_ERREXCEPT) &&
+ fetestexcept(FE_INVALID | FE_DIVBYZERO |
+ FE_OVERFLOW | FE_UNDERFLOW) != 0) {
+ /* Handle range error */
+ }
+ }
+
+ /* Use result... */
+}
+```
+
+## Noncompliant Code Example (pow())
+
+This noncompliant code example raises `x` to the power of `y`:
+
+```cpp
+#include
+
+void func(double x, double y) {
+ double result;
+ result = pow(x, y);
+}
+```
+This code may produce a domain error if `x` is negative and `y` is not an integer value or if `x` is 0 and `y` is 0. A domain error or pole error may occur if `x` is 0 and `y` is negative, and a range error may occur if the result cannot be represented as a `double`.
+
+## Compliant Solution (pow())
+
+Because the `pow()` function can produce domain errors, pole errors, and range errors, the programmer must first check that `x` and `y` lie within the proper domain and do not generate a pole error and then detect whether a range error occurs and act accordingly:
+
+```cpp
+#include
+#include
+#include
+
+void func(double x, double y) {
+ double result;
+
+ if (((x == 0.0f) && islessequal(y, 0.0)) || isless(x, 0.0)) {
+ /* Handle domain or pole error */
+ }
+
+ {
+ #pragma STDC FENV_ACCESS ON
+ if (math_errhandling & MATH_ERREXCEPT) {
+ feclearexcept(FE_ALL_EXCEPT);
+ }
+ errno = 0;
+
+ result = pow(x, y);
+
+ if ((math_errhandling & MATH_ERRNO) && errno != 0) {
+ /* Handle range error */
+ } else if ((math_errhandling & MATH_ERREXCEPT) &&
+ fetestexcept(FE_INVALID | FE_DIVBYZERO |
+ FE_OVERFLOW | FE_UNDERFLOW) != 0) {
+ /* Handle range error */
+ }
+ }
+
+ /* Use result... */
+}
+```
+
+## Noncompliant Code Example (asin(), Subnormal Number)
+
+This noncompliant code example determines the inverse sine of `x`:
+
+```cpp
+#include
+
+void func(float x) {
+ float result = asin(x);
+ /* ... */
+}
+```
+
+## Compliant Solution (asin(), Subnormal Number)
+
+Because this function has no domain errors but may have range errors, the programmer must detect a range error and act accordingly:
+
+```cpp
+#include
+#include
+#include
+void func(float x) {
+ float result;
+
+ {
+ #pragma STDC FENV_ACCESS ON
+ if (math_errhandling & MATH_ERREXCEPT) {
+ feclearexcept(FE_ALL_EXCEPT);
+ }
+ errno = 0;
+
+ result = asin(x);
+
+ if ((math_errhandling & MATH_ERRNO) && errno != 0) {
+ /* Handle range error */
+ } else if ((math_errhandling & MATH_ERREXCEPT) &&
+ fetestexcept(FE_INVALID | FE_DIVBYZERO |
+ FE_OVERFLOW | FE_UNDERFLOW) != 0) {
+ /* Handle range error */
+ }
+ }
+
+ /* Use result... */
+}
+```
+
+## Risk Assessment
+
+Failure to prevent or detect domain and range errors in math functions may cause unexpected results.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
FLP32-C | Medium | Probable | Medium | P8 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | stdlib-limits | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-FLP32 | Partially implemented |
CodeSonar | 7.2p0 | MATH.DOMAIN.ATANMATH.DOMAIN.TOOHIGHMATH.DOMAIN.TOOLOWMATH.DOMAINMATH.RANGEMATH.RANGE.GAMMAMATH.DOMAIN.LOGMATH.RANGE.LOGMATH.DOMAIN.FE_INVALIDMATH.DOMAIN.POWMATH.RANGE.COSH.TOOHIGHMATH.RANGE.COSH.TOOLOWMATH.DOMAIN.SQRT | Arctangent Domain Error Argument Too High Argument Too Low Floating Point Domain Error Floating Point Range Error Gamma on Zero Logarithm on Negative Value Logarithm on Zero Raises FE_INVALID Undefined Power of Zero cosh on High Number cosh on Low Number sqrt on Negative Value |
Helix QAC | 2022.4 | C5025 C++5033 | |
Parasoft C/C++test | 2022.2 | CERT_C-FLP32-a | Validate values passed to library functions |
PC-lint Plus | 1.4 | 2423 | Partially supported: reports domain errors for functions with the Semantics \*dom_1, \*dom_lt0, or \*dom_lt1, including standard library math functions |
Polyspace Bug Finder | R2022b | CERT-C: Rule FLP32-C | Checks for invalid use of standard library floating point routine (rule fully covered) |
PRQA QA-C | 9.7 | 5025 | |
PRQA QA-C++ | 4.4 | 5033 | |
RuleChecker | 22.04 | stdlib-limits | Partially checked |
TrustInSoft Analyzer | 1.38 | out-of-range argument | Partially verified. |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP32-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-391 and FLP32-C**
+
+Intersection( CWE-391, FLP32-C) =
+
+* Failure to detect range errors in floating-point calculations
+CWE-391 - FLP32-C
+* Failure to detect errors in functions besides floating-point calculations
+FLP32-C – CWE-391 =
+* Failure to detect domain errors in floating-point calculations
+**CWE-682 and FLP32-C**
+
+Independent( INT34-C, FLP32-C, INT33-C) CWE-682 = Union( FLP32-C, list) where list =
+
+* Incorrect calculations that do not involve floating-point range errors
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+This query identifies possible domain, pole and range errors on a selection of C standard library fuctions from math.h.
+
+## References
+
+* CERT-C: [FLP32-C: Prevent or detect domain and range errors in math functions](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.ql b/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.ql
new file mode 100644
index 0000000000..1e87aa3fae
--- /dev/null
+++ b/c/cert/src/rules/FLP32-C/UncheckedRangeDomainPoleErrors.ql
@@ -0,0 +1,27 @@
+/**
+ * @id c/cert/unchecked-range-domain-pole-errors
+ * @name FLP32-C: Prevent or detect domain and range errors in math functions
+ * @description Range, domain or pole errors in math functions may return unexpected values, trigger
+ * floating-point exceptions or set unexpected error modes.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/flp32-c
+ * correctness
+ * external/cert/severity/medium
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p8
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.uncheckedrangedomainpoleerrors.UncheckedRangeDomainPoleErrors
+
+class UncheckedRangeDomainPoleErrorsQuery extends UncheckedRangeDomainPoleErrorsSharedQuery {
+ UncheckedRangeDomainPoleErrorsQuery() {
+ this = FloatingTypesPackage::uncheckedRangeDomainPoleErrorsQuery()
+ }
+}
diff --git a/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.md b/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.md
new file mode 100644
index 0000000000..2ff0fcf262
--- /dev/null
+++ b/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.md
@@ -0,0 +1,181 @@
+# FLP34-C: Ensure that floating-point conversions are within range of the new type
+
+This query implements the CERT-C rule FLP34-C:
+
+> Ensure that floating-point conversions are within range of the new type
+
+
+## Description
+
+If a floating-point value is to be converted to a floating-point value of a smaller range and precision or to an integer type, or if an integer type is to be converted to a floating-point type, the value must be representable in the destination type.
+
+The C Standard, 6.3.1.4, paragraph 1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], says,
+
+> When a finite value of real floating type is converted to an integer type other than `_Bool`, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+
+Paragraph 2 of the same subclause says,
+
+> When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) manner. If the value being converted is outside the range of values that can be represented, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+
+And subclause 6.3.1.5, paragraph 1, says,
+
+> When a value of real floating type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) manner. If the value being converted is outside the range of values that can be represented, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
+
+
+See [undefined behaviors 17](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_17) and [18](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_18).
+
+This rule does not apply to demotions of floating-point types on [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) that support signed infinity, such as IEEE 754, as all values are within range.
+
+## Noncompliant Code Example (float to int)
+
+This noncompliant code example leads to undefined behavior if the integral part of `f_a` cannot be represented as an integer:
+
+```cpp
+void func(float f_a) {
+ int i_a;
+
+ /* Undefined if the integral part of f_a cannot be represented. */
+ i_a = f_a;
+}
+```
+
+## Compliant Solution (float to int)
+
+This compliant solution tests to ensure that the `float` value will fit within the `int` variable before performing the assignment.
+
+```cpp
+#include
+#include
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t); /* See INT35-C */
+#define PRECISION(umax_value) popcount(umax_value)
+
+void func(float f_a) {
+ int i_a;
+
+ if (isnan(f_a) ||
+ PRECISION(INT_MAX) < log2f(fabsf(f_a)) ||
+ (f_a != 0.0F && fabsf(f_a) < FLT_MIN)) {
+ /* Handle error */
+ } else {
+ i_a = f_a;
+ }
+}
+
+```
+
+## Noncompliant Code Example (Narrowing Conversion)
+
+This noncompliant code example attempts to perform conversions that may result in truncating values outside the range of the destination types:
+
+```cpp
+void func(double d_a, long double big_d) {
+ double d_b = (float)big_d;
+ float f_a = (float)d_a;
+ float f_b = (float)big_d;
+}
+
+```
+As a result of these conversions, it is possible that `d_a` is outside the range of values that can be represented by a float or that `big_d` is outside the range of values that can be represented as either a `float` or a `double`. If this is the case, the result is undefined on implementations that do not support Annex F, "IEC 60559 Floating-Point Arithmetic."
+
+## Compliant Solution (Narrowing Conversion)
+
+This compliant solution checks whether the values to be stored can be represented in the new type:
+
+```cpp
+#include
+#include
+
+void func(double d_a, long double big_d) {
+ double d_b;
+ float f_a;
+ float f_b;
+
+ if (d_a != 0.0 &&
+ (isnan(d_a) ||
+ isgreater(fabs(d_a), FLT_MAX) ||
+ isless(fabs(d_a), FLT_MIN))) {
+ /* Handle error */
+ } else {
+ f_a = (float)d_a;
+ }
+ if (big_d != 0.0 &&
+ (isnan(big_d) ||
+ isgreater(fabs(big_d), FLT_MAX) ||
+ isless(fabs(big_d), FLT_MIN))) {
+ /* Handle error */
+ } else {
+ f_b = (float)big_d;
+ }
+ if (big_d != 0.0 &&
+ (isnan(big_d) ||
+ isgreater(fabs(big_d), DBL_MAX) ||
+ isless(fabs(big_d), DBL_MIN))) {
+ /* Handle error */
+ } else {
+ d_b = (double)big_d;
+ }
+}
+
+```
+
+## Risk Assessment
+
+Converting a floating-point value to a floating-point value of a smaller range and precision or to an integer type, or converting an integer type to a floating-point type, can result in a value that is not representable in the destination type and is undefined behavior on implementations that do not support Annex F.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
FLP34-C | Low | Unlikely | Low | P3 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | | Supported Astrée reports all potential overflows resulting from floating-point conversions. |
Compass/ROSE | | | Can detect some violations of this rule. However, it does not flag implicit casts, only explicit ones |
CodeSonar | 7.2p0 | LANG.TYPE.IAT | Inappropriate Assignment Type |
Coverity | 2017.07 | MISRA_CAST (needs verification) | Can detect instances where implicit float conversion is involved: implicitly converting a complex expression with integer type to floating type, implicitly converting a double expression to narrower float type (may lose precision), implicitly converting a complex expression from float to double , implicitly converting from float to double in a function argument, and so on |
Helix QAC | 2022.4 | C4450, C4451, C4452, C4453, C4454, C4462, C4465 C++3011 | |
Klocwork | 2022.4 | MISRA.CAST.FLOAT.WIDER MISRA.CAST.FLOAT.INT MISRA.CAST.INT_FLOAT MISRA.CONV.FLOAT | |
LDRA tool suite | 9.7.1 | 435 S, 93 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-FLP34-aCERT_C-FLP34-b | Avoid implicit conversions from wider to narrower floating type Avoid implicit conversions of floating point numbers from wider to narrower floating type |
PC-lint Plus | 1.4 | 735, 736,915, 922,9118, 9227 | Partially supported |
Polyspace Bug Finder | R2022b | CERT C: Rule FLP34-C | Checks for float conversion overflow (rule partially covered) |
PRQA QA-C | 9.7 | 4450, 4451, 4452, 4453,4454, 4462, 4465 | Partially implemented |
PRQA QA-C++ | 4.4 | 3011 | |
PVS-Studio | 7.23 | V615 , V2003 , V2004 | |
TrustInSoft Analyzer | 1.38 | float_to_int | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP34-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-197 and FLP34-C**
+
+Independent( FLP34-C, INT31-C) FIO34-C = Subset( INT31-C)
+
+CWE-197 = Union( FLP34-C, INT31-C)
+
+**CWE-195 and FLP34-C**
+
+Intersection( CWE-195, FLP34-C) = Ø
+
+Both conditions involve type conversion. However, CWE-195 explicitly focuses on conversions between unsigned vs signed types, whereas FLP34-C focuses on floating-point arithmetic.
+
+**CWE-681 and FLP34-C**
+
+CWE-681 = Union( FLP34-C, INT31-C)
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [FLP34-C: Ensure that floating-point conversions are within range of the new type](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.ql b/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.ql
new file mode 100644
index 0000000000..eebc16afe3
--- /dev/null
+++ b/c/cert/src/rules/FLP34-C/UncheckedFloatingPointConversion.ql
@@ -0,0 +1,97 @@
+/**
+ * @id c/cert/unchecked-floating-point-conversion
+ * @name FLP34-C: Ensure that floating-point conversions are within range of the new type
+ * @description Conversions of out-of-range floating-point values to integral types can lead to
+ * undefined behavior.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/flp34-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/low
+ * external/cert/priority/p3
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.controlflow.Guards
+
+/*
+ * There are three cases to consider under this rule:
+ * 1) Float-to-int
+ * 2) Narrowing float-to-float conversions
+ * 3) Int-to-float
+ *
+ * The first results in undefined behaviour if the float is outside the range of the int, and is
+ * the topic of this query.
+ *
+ * The second two cases only cause undefined behaviour if the floating point format does not
+ * support -inf/+inf. This information is not definitively present in the CodeQL database. The
+ * macro INFINITY in principle differs in the two cases, but we are unable to distinguish one case
+ * from the other.
+ *
+ * (2) and (3) do not appear to be problems in practice on the hardware targets and compilers we
+ * support, because they all provide +inf and -inf unconditionally.
+ */
+
+/**
+ * A function whose name is suggestive that it counts the number of bits set.
+ */
+class PopCount extends Function {
+ PopCount() { this.getName().toLowerCase().matches("%popc%nt%") }
+}
+
+/**
+ * A macro which is suggestive that it is used to determine the precision of an integer.
+ */
+class PrecisionMacro extends Macro {
+ PrecisionMacro() { this.getName().toLowerCase().matches("precision") }
+}
+
+bindingset[value]
+predicate withinIntegralRange(IntegralType typ, float value) {
+ exists(float lb, float ub, float limit |
+ limit = 2.pow(8 * typ.getSize()) and
+ (
+ if typ.isUnsigned()
+ then (
+ lb = 0 and ub = limit - 1
+ ) else (
+ lb = -limit / 2 and
+ ub = (limit / 2) - 1
+ )
+ ) and
+ value >= lb and
+ value <= ub
+ )
+}
+
+from FloatingPointToIntegralConversion c, ArithmeticType underlyingTypeAfter
+where
+ not isExcluded(c, FloatingTypesPackage::uncheckedFloatingPointConversionQuery()) and
+ underlyingTypeAfter = c.getUnderlyingType() and
+ not (
+ // Either the upper or lower bound of the expression is outside the range of the new type
+ withinIntegralRange(underlyingTypeAfter, [upperBound(c.getExpr()), lowerBound(c.getExpr())])
+ or
+ // Heuristic - is there are guard the abs value of the float can fit in the precision of an int?
+ exists(GuardCondition gc, FunctionCall log2, FunctionCall fabs, Expr precision |
+ // gc.controls(c, false) and
+ log2.getTarget().hasGlobalOrStdName("log2" + ["", "l", "f"]) and
+ fabs.getTarget().hasGlobalOrStdName("fabs" + ["", "l", "f"]) and
+ log2.getArgument(0) = fabs and
+ // Precision is either a macro expansion or function call
+ (
+ precision.(FunctionCall).getTarget() instanceof PopCount
+ or
+ precision = any(PrecisionMacro pm).getAnInvocation().getExpr()
+ ) and
+ gc.ensuresLt(precision, log2, 0, c.getExpr().getBasicBlock(), false)
+ )
+ )
+select c, "Conversion of float to integer without appropriate guards avoiding undefined behavior."
diff --git a/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.md b/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.md
new file mode 100644
index 0000000000..b5602efa79
--- /dev/null
+++ b/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.md
@@ -0,0 +1,94 @@
+# FLP36-C: Preserve precision when converting integral values to floating-point type
+
+This query implements the CERT-C rule FLP36-C:
+
+> Preserve precision when converting integral values to floating-point type
+
+
+## Description
+
+Narrower arithmetic types can be cast to wider types without any effect on the magnitude of numeric values. However, whereas integer types represent exact values, floating-point types have limited precision. The C Standard, 6.3.1.4 paragraph 2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) manner. If the value being converted is outside the range of values that can be represented, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Results of some implicit conversions may be represented in greater range and precision than that required by the new type (see 6.3.1.8 and 6.8.6.4).
+
+
+Conversion from integral types to floating-point types without sufficient precision can lead to loss of precision (loss of least significant bits). No runtime exception occurs despite the loss.
+
+## Noncompliant Code Example
+
+In this noncompliant example, a large value of type `long int` is converted to a value of type `float` without ensuring it is representable in the type:
+
+```cpp
+#include
+
+int main(void) {
+ long int big = 1234567890L;
+ float approx = big;
+ printf("%ld\n", (big - (long int)approx));
+ return 0;
+}
+
+```
+For most floating-point hardware, the value closest to `1234567890` that is representable in type `float` is `1234567844`; consequently, this program prints the value `-46`.
+
+## Compliant Solution
+
+This compliant solution replaces the type `float` with a `double`. Furthermore, it uses an assertion to guarantee that the `double` type can represent any `long int` without loss of precision. (See [INT35-C. Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions) and [MSC11-C. Incorporate diagnostic tests using assertions](https://wiki.sei.cmu.edu/confluence/display/c/MSC11-C.+Incorporate+diagnostic+tests+using+assertions).)
+
+```cpp
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t); /* See INT35-C */
+#define PRECISION(umax_value) popcount(umax_value)
+
+int main(void) {
+ assert(PRECISION(LONG_MAX) <= DBL_MANT_DIG * log2(FLT_RADIX));
+ long int big = 1234567890L;
+ double approx = big;
+ printf("%ld\n", (big - (long int)approx));
+ return 0;
+}
+
+```
+On the same implementation, this program prints `0`, implying that the integer value `1234567890` is representable in type `double` without change.
+
+## Risk Assessment
+
+Conversion from integral types to floating-point types without sufficient precision can lead to loss of precision (loss of least significant bits).
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
FLP36-C | Low | Unlikely | Medium | P2 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | | Supported: Astrée keeps track of all floating point rounding errors and loss of precision and reports code defects resulting from those. |
CodeSonar | 7.2p0 | LANG.TYPE.IAT | Inappropriate Assignment Type |
Coverity | 2017.07 | MISRA C 2004 Rule 10.x (needs investigation) | Needs investigation |
Helix QAC | 2022.4 | C1260, C1263, C1298, C1299, C1800, C1802, C1803, C1804, C4117, C4435, C4437, C4445 C++3011 | |
Klocwork | 2022.4 | PORTING.CAST.FLTPNT | |
LDRA tool suite | 9.7.1 | 435 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-FLP36-a CERT_C-FLP36-b | Implicit conversions from integral to floating type which may result in a loss of information shall not be used Implicit conversions from integral constant to floating type which may result in a loss of information shall not be used |
PC-lint Plus | 1.4 | 915, 922 | Partially supported |
Polyspace Bug Finder | R2022b | CERT-C: Rule FLP36-C | Checks for precision loss in integer to float conversion (rule fully covered) |
PRQA QA-C | 9.7 | 1260, 1263, 1298, 1299, 1800, 1802, 1803, 1804, 4117, 4435, 4437, 4445 | |
PRQA QA-C++ | 4.4 | 3011 | |
PVS-Studio | 7.23 | V674 | |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP36-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [FLP36-C: Preserve precision when converting integral values to floating-point type](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.ql b/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.ql
new file mode 100644
index 0000000000..81e5670b11
--- /dev/null
+++ b/c/cert/src/rules/FLP36-C/IntToFloatPreservePrecision.ql
@@ -0,0 +1,74 @@
+/**
+ * @id c/cert/int-to-float-preserve-precision
+ * @name FLP36-C: Preserve precision when converting integral values to floating-point type
+ * @description Integer to floating-point conversions may lose precision if the floating-point type
+ * is unable to fully represent the integer value.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/flp36-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+
+/**
+ * Gets the maximum precise integral value for a floating point type, i.e. the maximum value that
+ * can be stored without loss of precision, and for which all smaller values can be stored without
+ * loss of precision.
+ *
+ * We make the assumption of a standard IEEE 754 floating point format and use the number of bits
+ * in the mantissa to determine the maximum value that can be stored precisely.
+ */
+float getMaxPreciseValue(FloatingPointType fp) {
+ // A 4-byte float has a 23-bit mantissa, but there is an implied leading 1, which makes a total
+ // of 24 bits, which can represent (2^24 -1) = 16,777,215 distinct values. However, 2^24 is also
+ // fully representable, so the maximum representable value is 2^24.
+ fp.getSize() = 4 and result = 2.pow(24)
+ or
+ // An 8-byte double has a 53-bit mantissa, similar logic to the above.
+ fp.getSize() = 8 and result = 2.pow(53)
+}
+
+from
+ IntegralToFloatingPointConversion c, float maxPreciseValue, string message,
+ FloatingPointType targetType
+where
+ not isExcluded(c, FloatingTypesPackage::intToFloatPreservePrecisionQuery()) and
+ targetType = c.getType() and
+ // Get the maximum value for which all smaller values can be stored precisely
+ maxPreciseValue = getMaxPreciseValue(targetType) and
+ (
+ // Find the upper bound, and determine if it is greater than the maximum value that can be
+ // stored precisely.
+ // Note: the range analysis also works on floats (doubles), which means that it also loses
+ // precision at the end of the 64 bit mantissa range.
+ exists(float upper | upper = upperBound(c.getExpr()) |
+ upper > maxPreciseValue and
+ message =
+ "The upper bound of this value (" + upper + ") cast from " + c.getExpr().getType() + " to " +
+ targetType + " is greater than the maximum value (" + maxPreciseValue +
+ ") that can be stored precisely."
+ )
+ or
+ // Find the lower bound, and determine if it is less than the negative maximum value that can
+ // be stored precisely.
+ // Note: the range analysis also works on floats (doubles), which means that it also loses
+ // precision at the end of the 64 bit mantissa range.
+ exists(float lower | lower = lowerBound(c.getExpr()) |
+ lower < -maxPreciseValue and
+ message =
+ "The lower bound of this value (" + lower + ") cast from " + c.getExpr().getType() + " to " +
+ targetType + " is smaller than the minimum value (" + -maxPreciseValue +
+ ") that can be stored precisely."
+ )
+ )
+select c, message
diff --git a/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.md b/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.md
new file mode 100644
index 0000000000..4713696d6f
--- /dev/null
+++ b/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.md
@@ -0,0 +1,88 @@
+# FLP37-C: Do not use object representations to compare floating-point values
+
+This query implements the CERT-C rule FLP37-C:
+
+> Do not use object representations to compare floating-point values
+
+
+## Description
+
+The object representation for floating-point values is implementation defined. However, an implementation that defines the `__STDC_IEC_559__` macro shall conform to the IEC 60559 floating-point standard and uses what is frequently referred to as IEEE 754 floating-point arithmetic \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\]. The floating-point object representation used by IEC 60559 is one of the most common floating-point object representations in use today.
+
+All floating-point object representations use specific bit patterns to encode the value of the floating-point number being represented. However, equivalence of floating-point values is not encoded solely by the bit pattern used to represent the value. For instance, if the floating-point format supports negative zero values (as IEC 60559 does), the values `-0.0` and `0.0` are equivalent and will compare as equal, but the bit patterns used in the object representation are not identical. Similarly, if two floating-point values are both (the same) NaN, they will not compare as equal, despite the bit patterns being identical, because they are not equivalent.
+
+Do not compare floating-point object representations directly, such as by calling `memcmp()`or its moral equivalents. Instead, the equality operators (`==` and `!=`) should be used to determine if two floating-point values are equivalent.
+
+## Noncompliant Code Example
+
+In this noncompliant code example, `memcmp()` is used to compare two structures for equality. However, since the structure contains a floating-point object, this code may not behave as the programmer intended.
+
+```cpp
+#include
+#include
+
+struct S {
+ int i;
+ float f;
+};
+
+bool are_equal(const struct S *s1, const struct S *s2) {
+ if (!s1 && !s2)
+ return true;
+ else if (!s1 || !s2)
+ return false;
+ return 0 == memcmp(s1, s2, sizeof(struct S));
+}
+```
+
+## Compliant Solution
+
+In this compliant solution, the structure members are compared individually:
+
+```cpp
+#include
+#include
+
+struct S {
+ int i;
+ float f;
+};
+
+bool are_equal(const struct S *s1, const struct S *s2) {
+ if (!s1 && !s2)
+ return true;
+ else if (!s1 || !s2)
+ return false;
+ return s1->i == s2->i &&
+ s1->f == s2->f;
+}
+```
+
+## Risk Assessment
+
+Using the object representation of a floating-point value for comparisons can lead to incorrect equality results, which can lead to unexpected behavior.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
FLP37-C | Low | Unlikely | Medium | P2 | L3 |
+
+
+## Automated Detection
+
+
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP37-C).
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [FLP37-C: Do not use object representations to compare floating-point values](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.ql b/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.ql
new file mode 100644
index 0000000000..8735a804fa
--- /dev/null
+++ b/c/cert/src/rules/FLP37-C/MemcmpUsedToCompareFloats.ql
@@ -0,0 +1,41 @@
+/**
+ * @id c/cert/memcmp-used-to-compare-floats
+ * @name FLP37-C: Do not use object representations to compare floating-point values
+ * @description Comparing floating point values using the memcmp can lead to unexpected comparison
+ * failures as equivalent floating-point values may not have the same bit pattern.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/flp37-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.security.BufferAccess
+
+/**
+ * A type which contains, directly or indirectly, a floating-point type.
+ */
+class FloatContainingType extends Type {
+ FloatContainingType() {
+ this instanceof FloatingPointType
+ or
+ this.(Class).getAField().getType().getUnspecifiedType() instanceof FloatContainingType
+ }
+}
+
+from MemcmpBA cmp, string buffDesc, Expr arg, FloatContainingType type
+where
+ not isExcluded(cmp, FloatingTypesPackage::memcmpUsedToCompareFloatsQuery()) and
+ arg = cmp.getBuffer(buffDesc, _) and
+ arg.getUnconverted().getUnspecifiedType().(PointerType).getBaseType() = type
+select cmp,
+ "memcmp is used to compare a floating-point value in the $@ which is of type " + type + ".", arg,
+ buffDesc
diff --git a/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.md b/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.md
new file mode 100644
index 0000000000..d57756b4b5
--- /dev/null
+++ b/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.md
@@ -0,0 +1,249 @@
+# INT30-C: Ensure that unsigned integer operations do not wrap
+
+This query implements the CERT-C rule INT30-C:
+
+> Ensure that unsigned integer operations do not wrap
+
+
+## Description
+
+The C Standard, 6.2.5, paragraph 9 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
+
+
+This behavior is more informally called [unsigned integer wrapping](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unsignedintegerwrapping). Unsigned integer operations can wrap if the resulting value cannot be represented by the underlying representation of the integer. The following table indicates which operators can result in wrapping:
+
+ Operator | Wrap | Operator | Wrap | Operator | Wrap | Operator | Wrap |
+ | Yes | -= | Yes | << | Yes | < | No |
- | Yes | \*= | Yes | >> | No | > | No |
\* | Yes | /= | No | & | No | >= | No |
/ | No | %= | No | | | No | <= | No |
% | No | <<= | Yes | ^ | No | == | No |
++ | Yes | >>= | No | ~ | No | != | No |
-- | Yes | &= | No | ! | No | && | No |
= | No | |= | No | un + | No | || | No |
+= | Yes | ^= | No | un - | Yes | ?: | No |
+The following sections examine specific operations that are susceptible to unsigned integer wrap. When operating on integer types with less precision than `int`, integer promotions are applied. The usual arithmetic conversions may also be applied to (implicitly) convert operands to equivalent types before arithmetic operations are performed. Programmers should understand integer conversion rules before trying to implement secure arithmetic operations. (See [INT02-C. Understand integer conversion rules](https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules).)
+
+
+Integer values must not be allowed to wrap, especially if they are used in any of the following ways:
+
+* Integer operands of any pointer arithmetic, including array indexing
+* The assignment expression for the declaration of a variable length array
+* The postfix expression preceding square brackets `[]` or the expression in square brackets `[]` of a subscripted designation of an element of an array object
+* Function arguments of type `size_t` or `rsize_t` (for example, an argument to a memory allocation function)
+* In security-critical code
+The C Standard defines arithmetic on atomic integer types as read-modify-write operations with the same representation as regular integer types. As a result, wrapping of atomic unsigned integers is identical to regular unsigned integers and should also be prevented or detected.
+
+## Addition
+
+Addition is between two operands of arithmetic type or between a pointer to an object type and an integer type. This rule applies only to addition between two operands of arithmetic type. (See [ARR37-C. Do not add or subtract an integer to a pointer to a non-array object](https://wiki.sei.cmu.edu/confluence/display/c/ARR37-C.+Do+not+add+or+subtract+an+integer+to+a+pointer+to+a+non-array+object) and [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts).)
+
+Incrementing is equivalent to adding 1.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in an unsigned integer wrap during the addition of the unsigned operands `ui_a` and `ui_b`. If this behavior is [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior), the resulting value may be used to allocate insufficient memory for a subsequent operation or in some other manner that can lead to an exploitable [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability).
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int usum = ui_a + ui_b;
+ /* ... */
+}
+```
+**Compliant Solution (Precondition Test)**
+
+This compliant solution performs a precondition test of the operands of the addition to guarantee there is no possibility of unsigned wrap:
+
+```cpp
+#include
+
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int usum;
+ if (UINT_MAX - ui_a < ui_b) {
+ /* Handle error */
+ } else {
+ usum = ui_a + ui_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution (Postcondition Test)**
+
+This compliant solution performs a postcondition test to ensure that the result of the unsigned addition operation `usum` is not less than the first operand:
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int usum = ui_a + ui_b;
+ if (usum < ui_a) {
+ /* Handle error */
+ }
+ /* ... */
+}
+```
+
+## Subtraction
+
+Subtraction is between two operands of arithmetic type, two pointers to qualified or unqualified versions of compatible object types, or a pointer to an object type and an integer type. This rule applies only to subtraction between two operands of arithmetic type. (See [ARR36-C. Do not subtract or compare two pointers that do not refer to the same array](https://wiki.sei.cmu.edu/confluence/display/c/ARR36-C.+Do+not+subtract+or+compare+two+pointers+that+do+not+refer+to+the+same+array), [ARR37-C. Do not add or subtract an integer to a pointer to a non-array object](https://wiki.sei.cmu.edu/confluence/display/c/ARR37-C.+Do+not+add+or+subtract+an+integer+to+a+pointer+to+a+non-array+object), and [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts) for information about pointer subtraction.)
+
+Decrementing is equivalent to subtracting 1.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in an unsigned integer wrap during the subtraction of the unsigned operands `ui_a` and `ui_b`. If this behavior is unanticipated, it may lead to an exploitable [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability).
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int udiff = ui_a - ui_b;
+ /* ... */
+}
+```
+**Compliant Solution (Precondition Test)**
+
+This compliant solution performs a precondition test of the unsigned operands of the subtraction operation to guarantee there is no possibility of unsigned wrap:
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int udiff;
+ if (ui_a < ui_b){
+ /* Handle error */
+ } else {
+ udiff = ui_a - ui_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution (Postcondition Test)**
+
+This compliant solution performs a postcondition test that the result of the unsigned subtraction operation `udiff` is not greater than the minuend:
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int udiff = ui_a - ui_b;
+ if (udiff > ui_a) {
+ /* Handle error */
+ }
+ /* ... */
+}
+```
+
+## Multiplication
+
+Multiplication is between two operands of arithmetic type.
+
+**Noncompliant Code Example**
+
+The Mozilla Foundation Security Advisory 2007-01 describes a heap buffer overflow vulnerability in the Mozilla Scalable Vector Graphics (SVG) viewer resulting from an unsigned integer wrap during the multiplication of the `signed int` value `pen->num_vertices` and the `size_t` value `sizeof(cairo_pen_vertex_t)` \[[VU\#551436](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-VU551436)\]. The `signed int` operand is converted to `size_t` prior to the multiplication operation so that the multiplication takes place between two `size_t` integers, which are unsigned. (See [INT02-C. Understand integer conversion rules](https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules).)
+
+```cpp
+pen->num_vertices = _cairo_pen_vertices_needed(
+ gstate->tolerance, radius, &gstate->ctm
+);
+pen->vertices = malloc(
+ pen->num_vertices * sizeof(cairo_pen_vertex_t)
+);
+
+```
+The unsigned integer wrap can result in allocating memory of insufficient size.
+
+**Compliant Solution**
+
+This compliant solution tests the operands of the multiplication to guarantee that there is no unsigned integer wrap:
+
+```cpp
+pen->num_vertices = _cairo_pen_vertices_needed(
+ gstate->tolerance, radius, &gstate->ctm
+);
+
+if (pen->num_vertices > SIZE_MAX / sizeof(cairo_pen_vertex_t)) {
+ /* Handle error */
+}
+pen->vertices = malloc(
+ pen->num_vertices * sizeof(cairo_pen_vertex_t)
+);
+
+```
+
+## Exceptions
+
+**INT30-C-EX1:** Unsigned integers can exhibit modulo behavior (wrapping) when necessary for the proper execution of the program. It is recommended that the variable declaration be clearly commented as supporting modulo behavior and that each operation on that integer also be clearly commented as supporting modulo behavior.
+
+**INT30-C-EX2:** Checks for wraparound can be omitted when it can be determined at compile time that wraparound will not occur. As such, the following operations on unsigned integers require no validation:
+
+* Operations on two compile-time constants
+* Operations on a variable and 0 (except division or remainder by 0)
+* Subtracting any variable from its type's maximum; for example, any `unsigned int` may safely be subtracted from `UINT_MAX`
+* Multiplying any variable by 1
+* Division or remainder, as long as the divisor is nonzero
+* Right-shifting any type maximum by any number no larger than the type precision; for example, `UINT_MAX >> x` is valid as long as `0 <= x < 32` (assuming that the precision of `unsigned int` is 32 bits)
+**INT30-C-EX3.** The left-shift operator takes two operands of integer type. Unsigned left shift `<<` can exhibit modulo behavior (wrapping). This exception is provided because of common usage, because this behavior is usually expected by the programmer, and because the behavior is well defined. For examples of usage of the left-shift operator, see [INT34-C. Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand](https://wiki.sei.cmu.edu/confluence/display/c/INT34-C.+Do+not+shift+an+expression+by+a+negative+number+of+bits+or+by+greater+than+or+equal+to+the+number+of+bits+that+exist+in+the+operand).
+
+## Risk Assessment
+
+Integer wrap can lead to buffer overflows and the execution of arbitrary code by an attacker.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT30-C | High | Likely | High | P9 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | integer-overflow | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-INT30 | Implemented |
CodeSonar | 7.2p0 | ALLOC.SIZE.ADDOFLOW ALLOC.SIZE.IOFLOW ALLOC.SIZE.MULOFLOW ALLOC.SIZE.SUBUFLOW MISC.MEM.SIZE.ADDOFLOW MISC.MEM.SIZE.BAD MISC.MEM.SIZE.MULOFLOW MISC.MEM.SIZE.SUBUFLOW | Addition overflow of allocation size Integer overflow of allocation size Multiplication overflow of allocation size Subtraction underflow of allocation size Addition overflow of size Unreasonable size argument Multiplication overflow of size Subtraction underflow of size |
Compass/ROSE | | | Can detect violations of this rule by ensuring that operations are checked for overflow before being performed (Be mindful of exception INT30-EX2 because it excuses many operations from requiring validation , including all the operations that would validate a potentially dangerous operation. For instance, adding two unsigned int s together requires validation involving subtracting one of the numbers from UINT_MAX , which itself requires no validation because it cannot wrap.) |
Coverity | 2017.07 | INTEGER_OVERFLOW | Implemented |
Helix QAC | 2022.4 | C2910, C3383, C3384, C3385, C3386 C++2910 DF2911, DF2912, DF2913, | |
Klocwork | 2022.4 | NUM.OVERFLOW CWARN.NOEFFECT.OUTOFRANGE NUM.OVERFLOW.DF | |
LDRA tool suite | 9.7.1 | 493 S, 494 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT30-a CERT_C-INT30-b CERT_C-INT30-c | Avoid integer overflows Integer overflow or underflow in constant expression in '+', '-', '\*' operator Integer overflow or underflow in constant expression in '<<' operator |
Polyspace Bug Finder | R2022b | CERT C: Rule INT30-C | Checks for: Unsigned integer overflownsigned integer overflow, unsigned integer constant overflownsigned integer constant overflow. Rule partially covered. |
PRQA QA-C | 9.7 | 2910 \[C\], 2911 \[D\], 2912 \[A\], 2913 \[S\], 3383, 3384, 3385, 3386 | Partially implemented |
PRQA QA-C++ | 4.4 | 2910, 2911, 2912, 2913 | |
PVS-Studio | 7.23 | V658, V1012, V1028, V5005, V5011 | |
TrustInSoft Analyzer | 1.38 | unsigned overflow | Exhaustively verified. |
+
+
+## Related Vulnerabilities
+
+[CVE-2009-1385](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1385) results from a violation of this rule. The value performs an unchecked subtraction on the `length` of a buffer and then adds those many bytes of data to another buffer \[[xorl 2009](http://xorl.wordpress.com/2009/06/10/cve-2009-1385-linux-kernel-e1000-integer-underflow/)\]. This can cause a buffer overflow, which allows an attacker to execute arbitrary code.
+
+A Linux Kernel vmsplice [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit), described by Rafal Wojtczuk \[[Wojtczuk 2008](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Wojtczuk08)\], documents a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) and exploit arising from a buffer overflow (caused by unsigned integer wrapping).
+
+Don Bailey \[[Bailey 2014](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Bailey14)\] describes an unsigned integer wrap [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in the LZO compression algorithm, which can be exploited in some implementations.
+
+[CVE-2014-4377](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-4377) describes a [vulnerability](http://blog.binamuse.com/2014/09/coregraphics-memory-corruption.html) in iOS 7.1 resulting from a multiplication operation that wraps, producing an insufficiently small value to pass to a memory allocation routine, which is subsequently overflowed.
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT30-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-131 and INT30-C**
+
+* Intersection( INT30-C, MEM35-C) = Ø
+* Intersection( CWE-131, INT30-C) =
+* Calculating a buffer size such that the calculation wraps. This can happen, for example, when using malloc() or operator new\[\] to allocate an array, multiplying the array item size with the array dimension. An untrusted dimension could cause wrapping, resulting in a too-small buffer being allocated, and subsequently overflowed when the array is initialized.
+* CWE-131 – INT30-C =
+* Incorrect calculation of a buffer size that does not involve wrapping. This includes off-by-one errors, for example.
+INT30-C – CWE-131 =
+* Integer wrapping where the result is not used to allocate memory.
+**CWE-680 and INT30-C**
+
+Intersection( CWE-680, INT30-C) =
+
+* Unsigned integer overflows that lead to buffer overflows
+CWE-680 - INT30-C =
+* Signed integer overflows that lead to buffer overflows
+INT30-C – CWE-680 =
+* Unsigned integer overflows that do not lead to buffer overflows
+**CWE-191 and INT30-C**
+
+Union( CWE-190, CWE-191) = Union( INT30-C, INT32-C) Intersection( INT30-C, INT32-C) == Ø
+
+Intersection(CWE-191, INT30-C) =
+
+* Underflow of unsigned integer operation
+CWE-191 – INT30-C =
+* Underflow of signed integer operation
+INT30-C – CWE-191 =
+* Overflow of unsigned integer operation
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT30-C: Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.ql b/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.ql
new file mode 100644
index 0000000000..c893584a1e
--- /dev/null
+++ b/c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.ql
@@ -0,0 +1,30 @@
+/**
+ * @id c/cert/unsigned-integer-operations-wrap-around
+ * @name INT30-C: Ensure that unsigned integer operations do not wrap
+ * @description Unsigned integer expressions do not strictly overflow, but instead wrap around in a
+ * modular way. If the size of the type is not sufficient, this can happen
+ * unexpectedly.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/int30-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p9
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.rules.unsignedoperationwithconstantoperandswraps.UnsignedOperationWithConstantOperandsWraps
+
+class UnsignedIntegerOperationsWrapAroundQuery extends UnsignedOperationWithConstantOperandsWrapsSharedQuery
+{
+ UnsignedIntegerOperationsWrapAroundQuery() {
+ this = IntegerOverflowPackage::unsignedIntegerOperationsWrapAroundQuery()
+ }
+}
diff --git a/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.md b/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.md
new file mode 100644
index 0000000000..50e0bfdbe0
--- /dev/null
+++ b/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.md
@@ -0,0 +1,361 @@
+# INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data
+
+This query implements the CERT-C rule INT31-C:
+
+> Ensure that integer conversions do not result in lost or misinterpreted data
+
+
+## Description
+
+Integer conversions, both implicit and explicit (using a cast), must be guaranteed not to result in lost or misinterpreted data. This rule is particularly true for integer values that originate from untrusted sources and are used in any of the following ways:
+
+* Integer operands of any pointer arithmetic, including array indexing
+* The assignment expression for the declaration of a variable length array
+* The postfix expression preceding square brackets `[]` or the expression in square brackets `[]` of a subscripted designation of an element of an array object
+* Function arguments of type `size_t` or `rsize_t` (for example, an argument to a memory allocation function)
+This rule also applies to arguments passed to the following library functions that are converted to `unsigned char`:
+* `memset()`
+* `memset_s()`
+* `fprintf()` and related functions (For the length modifier `c`, if no `l` length modifier is present, the `int` argument is converted to an `unsigned char`, and the resulting character is written.)
+* `fputc()`
+* `ungetc()`
+* `memchr()`
+and to arguments to the following library functions that are converted to `char`:
+* `strchr()`
+* `strrchr()`
+* All of the functions listed in ``
+The only integer type conversions that are guaranteed to be safe for all data values and all possible conforming implementations are conversions of an integral value to a wider type of the same signedness. The C Standard, subclause 6.3.1.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IECTR24731-2-2010)\], says
+
+> When a value with integer type is converted to another integer type other than `_Bool`, if the value can be represented by the new type, it is unchanged.
+
+
+Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
+
+Otherwise, the new type is signed and the value cannot be represented in it; either the result is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) or an implementation-defined signal is raised.
+
+Typically, converting an integer to a smaller type results in truncation of the high-order bits.
+
+## Noncompliant Code Example (Unsigned to Signed)
+
+Type range errors, including loss of data (truncation) and loss of sign (sign errors), can occur when converting from a value of an unsigned integer type to a value of a signed integer type. This noncompliant code example results in a truncation error on most [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation):
+
+```cpp
+#include
+
+void func(void) {
+ unsigned long int u_a = ULONG_MAX;
+ signed char sc;
+ sc = (signed char)u_a; /* Cast eliminates warning */
+ /* ... */
+}
+```
+
+## Compliant Solution (Unsigned to Signed)
+
+Validate ranges when converting from an unsigned type to a signed type. This compliant solution can be used to convert a value of `unsigned long int` type to a value of `signed char `type:
+
+```cpp
+#include
+
+void func(void) {
+ unsigned long int u_a = ULONG_MAX;
+ signed char sc;
+ if (u_a <= SCHAR_MAX) {
+ sc = (signed char)u_a; /* Cast eliminates warning */
+ } else {
+ /* Handle error */
+ }
+}
+```
+
+## Noncompliant Code Example (Signed to Unsigned)
+
+Type range errors, including loss of data (truncation) and loss of sign (sign errors), can occur when converting from a value of a signed type to a value of an unsigned type. This noncompliant code example results in a negative number being misinterpreted as a large positive number.
+
+```cpp
+#include
+
+void func(signed int si) {
+ /* Cast eliminates warning */
+ unsigned int ui = (unsigned int)si;
+
+ /* ... */
+}
+
+/* ... */
+
+func(INT_MIN);
+```
+
+## Compliant Solution (Signed to Unsigned)
+
+Validate ranges when converting from a signed type to an unsigned type. This compliant solution converts a value of a `signed int` type to a value of an `unsigned int` type:
+
+```cpp
+#include
+
+void func(signed int si) {
+ unsigned int ui;
+ if (si < 0) {
+ /* Handle error */
+ } else {
+ ui = (unsigned int)si; /* Cast eliminates warning */
+ }
+ /* ... */
+}
+/* ... */
+
+func(INT_MIN + 1);
+```
+Subclause 6.2.5, paragraph 9, of the C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IECTR24731-2-2010)\] provides the necessary guarantees to ensure this solution works on a [conforming](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-conformingprogram) [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation):
+
+> The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same.
+
+
+## Noncompliant Code Example (Signed, Loss of Precision)
+
+A loss of data (truncation) can occur when converting from a value of a signed integer type to a value of a signed type with less precision. This noncompliant code example results in a truncation error on most [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation):
+
+```cpp
+#include
+
+void func(void) {
+ signed long int s_a = LONG_MAX;
+ signed char sc = (signed char)s_a; /* Cast eliminates warning */
+ /* ... */
+}
+```
+
+## Compliant Solution (Signed, Loss of Precision)
+
+Validate ranges when converting from a signed type to a signed type with less precision. This compliant solution converts a value of a `signed long int` type to a value of a `signed char` type:
+
+```cpp
+#include
+
+void func(void) {
+ signed long int s_a = LONG_MAX;
+ signed char sc;
+ if ((s_a < SCHAR_MIN) || (s_a > SCHAR_MAX)) {
+ /* Handle error */
+ } else {
+ sc = (signed char)s_a; /* Use cast to eliminate warning */
+ }
+ /* ... */
+}
+
+```
+Conversions from a value of a signed integer type to a value of a signed integer type with less precision requires that both the upper and lower bounds are checked.
+
+## Noncompliant Code Example (Unsigned, Loss of Precision)
+
+A loss of data (truncation) can occur when converting from a value of an unsigned integer type to a value of an unsigned type with less precision. This noncompliant code example results in a truncation error on most [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation):
+
+```cpp
+#include
+
+void func(void) {
+ unsigned long int u_a = ULONG_MAX;
+ unsigned char uc = (unsigned char)u_a; /* Cast eliminates warning */
+ /* ... */
+}
+```
+
+## Compliant Solution (Unsigned, Loss of Precision)
+
+Validate ranges when converting a value of an unsigned integer type to a value of an unsigned integer type with less precision. This compliant solution converts a value of an `unsigned long int` type to a value of an `unsigned char` type:
+
+```cpp
+#include
+
+void func(void) {
+ unsigned long int u_a = ULONG_MAX;
+ unsigned char uc;
+ if (u_a > UCHAR_MAX) {
+ /* Handle error */
+ } else {
+ uc = (unsigned char)u_a; /* Cast eliminates warning */
+ }
+ /* ... */
+}
+
+```
+Conversions from unsigned types with greater precision to unsigned types with less precision require only the upper bounds to be checked.
+
+## Noncompliant Code Example (time_t Return Value)
+
+The `time()` function returns the value `(time_t)(-1)` to indicate that the calendar time is not available. The C Standard requires that the `time_t` type is only a *real type* capable of representing time. (The integer and real floating types are collectively called real types.) It is left to the implementor to decide the best real type to use to represent time. If `time_t` is implemented as an unsigned integer type with less precision than a signed `int`, the return value of `time()` will never compare equal to the integer literal `-1`.
+
+```cpp
+#include
+
+void func(void) {
+ time_t now = time(NULL);
+ if (now != -1) {
+ /* Continue processing */
+ }
+}
+```
+
+## Compliant Solution (time_t Return Value)
+
+To ensure the comparison is properly performed, the return value of `time()` should be compared against `-1` cast to type `time_t`:
+
+```cpp
+#include
+
+void func(void) {
+ time_t now = time(NULL);
+ if (now != (time_t)-1) {
+ /* Continue processing */
+ }
+}
+```
+This solution is in accordance with [INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size](https://wiki.sei.cmu.edu/confluence/display/c/INT18-C.+Evaluate+integer+expressions+in+a+larger+size+before+comparing+or+assigning+to+that+size). Note that `(time_+t)-1` also complies with **INT31-C-EX3**.
+
+## Noncompliant Code Example (memset())
+
+For historical reasons, certain C Standard functions accept an argument of type `int` and convert it to either `unsigned char` or plain `char`. This conversion can result in unexpected behavior if the value cannot be represented in the smaller type. The second argument to `memset()` is an example; it indicates what byte to store in the range of memory indicated by the first and third arguments. If the second argument is outside the range of a `signed char` or plain `char`, then its higher order bits will typically be truncated. Consequently, this noncompliant solution unexpectedly sets all elements in the array to 0, rather than 4096:
+
+```cpp
+#include
+#include
+
+int *init_memory(int *array, size_t n) {
+ return memset(array, 4096, n);
+}
+```
+
+## Compliant Solution (memset())
+
+In general, the `memset()` function should not be used to initialize an integer array unless it is to set or clear all the bits, as in this compliant solution:
+
+```cpp
+#include
+#include
+
+int *init_memory(int *array, size_t n) {
+ return memset(array, 0, n);
+}
+```
+
+## Exceptions
+
+**INT31-C-EX1:** The C Standard defines minimum ranges for standard integer types. For example, the minimum range for an object of type `unsigned short int` is 0 to 65,535, whereas the minimum range for `int` is −32,767 to +32,767. Consequently, it is not always possible to represent all possible values of an `unsigned short int` as an `int`. However, on the IA-32 architecture, for example, the actual integer range is from −2,147,483,648 to +2,147,483,647, meaning that it is quite possible to represent all the values of an `unsigned short int` as an `int` for this architecture. As a result, it is not necessary to provide a test for this conversion on IA-32. It is not possible to make assumptions about conversions without knowing the precision of the underlying types. If these tests are not provided, assumptions concerning precision must be clearly documented, as the resulting code cannot be safely ported to a system where these assumptions are invalid. A good way to document these assumptions is to use static assertions. (See [DCL03-C. Use a static assertion to test the value of a constant expression](https://wiki.sei.cmu.edu/confluence/display/c/DCL03-C.+Use+a+static+assertion+to+test+the+value+of+a+constant+expression).)
+
+**INT31-C-EX2:** Conversion from any integer type with a value between `SCHAR_MIN` and `UCHAR_MAX` to a character type is permitted provided the value represents a character and not an integer.
+
+Conversions to unsigned character types are well defined by C to have modular behavior. A character's value is not misinterpreted by the loss of sign or conversion to a negative number. For example, the Euro symbol `€` is sometimes represented by bit pattern `0x80` which can have the numerical value 128 or −127 depending on the signedness of the type.
+
+Conversions to signed character types are more problematic. The C Standard, subclause 6.3.1.3, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IECTR24731-2-2010)\], says, regarding conversions
+
+> Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
+
+
+Furthermore, subclause 6.2.6.2, paragraph 2, says, regarding integer modifications
+
+> If the sign bit is one, the value shall be modified in one of the following ways:— the corresponding value with sign bit 0 is negated (sign and magnitude)— the sign bit has the value −(2M ) (two’s complement);— the sign bit has the value −(2M − 1) (ones’ complement).Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value. \[See note.\]
+
+
+NOTE: *Two's complement* is shorthand for "radix complement in radix 2." *Ones' complement* is shorthand for "diminished radix complement in radix 2."
+
+Consequently, the standard allows for this code to trap:
+
+```cpp
+int i = 128; /* 1000 0000 in binary */
+assert(SCHAR_MAX == 127);
+signed char c = i; /* can trap */
+
+```
+However, platforms where this code traps or produces an unexpected value are rare. According to *[The New C Standard: An Economic and Cultural Commentary](http://www.knosof.co.uk/cbook/cbook.html)* by Derek Jones \[[Jones 2008](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Jones08)\],
+
+> Implementations with such trap representations are thought to have existed in the past. Your author was unable to locate any documents describing such processors.
+
+
+**INT31-C-EX3:** ISO C, section 7.27.2.4, paragraph 3 says:
+
+> The time function returns the implementation’s best approximation to the current calendar time.
+
+
+The value (time_t) (−1) is returned if the calendar time is not available.
+
+If `time_t` is an unsigned type, then the expression `((time_t) (-1))` is guaranteed to yield a large positive value.
+
+Therefore, conversion of a negative compile-time constant to an unsigned value with the same or larger width is permitted by this rule. This exception does not apply to conversion of unsigned to signed values, nor does it apply if the resulting value would undergo truncation.
+
+## Risk Assessment
+
+Integer truncation errors can lead to buffer overflows and the execution of arbitrary code by an attacker.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT31-C | High | Probable | High | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | | Supported via MISRA C:2012 Rules 10.1, 10.3, 10.4, 10.6 and 10.7 |
CodeSonar | 7.2p0 | LANG.CAST.PC.AVLANG.CAST.PC.CONST2PTRLANG.CAST.PC.INT LANG.CAST.COERCELANG.CAST.VALUE ALLOC.SIZE.TRUNCMISC.MEM.SIZE.TRUNC LANG.MEM.TBA | Cast: arithmetic type/void pointer Conversion: integer constant to pointer Conversion: pointer/integer Coercion alters value Cast alters value Truncation of allocation size Truncation of size Tainted buffer access |
Compass/ROSE | | | Can detect violations of this rule. However, false warnings may be raised if limits.h is included |
Coverity \* | 2017.07 | NEGATIVE_RETURNS REVERSE_NEGATIVE MISRA_CAST | Can find array accesses, loop bounds, and other expressions that may contain dangerous implied integer conversions that would result in unexpected behavior Can find instances where a negativity check occurs after the negative value has been used for something else Can find instances where an integer expression is implicitly converted to a narrower integer type, where the signedness of an integer value is implicitly converted, or where the type of a complex expression is implicitly converted |
Cppcheck | 1.66 | memsetValueOutOfRange | The second argument to memset() cannot be represented as unsigned char |
Helix QAC | 2022.4 | C2850, C2855, C2890, C2895, C2900, C2905, C++2850, C++2855, C++2890, C++2895, C++2900, C++2905, C++3000, C++3010 DF2851, DF2852, DF2853, DF2856, DF2857, DF2858, DF2891, DF2892, DF2893, DF2896, DF2897, DF2898, DF2901, DF2902, DF2903, DF2906, DF2907, DF2908 | |
Klocwork | 2022.4 | PORTING.CAST.SIZE | |
LDRA tool suite | 9.7.1 | 93 S , 433 S , 434 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT31-a CERT_C-INT31-b CERT_C-INT31-c CERT_C-INT31-d CERT_C-INT31-e CERT_C-INT31-f CERT_C-INT31-g CERT_C-INT31-h CERT_C-INT31-i CERT_C-INT31-j CERT_C-INT31-k CERT_C-INT31-l CERT_C-INT31-m CERT_C-INT31-nCERT_C-INT31-o | An expression of essentially Boolean type should always be used where an operand is interpreted as a Boolean value An operand of essentially Boolean type should not be used where an operand is interpreted as a numeric value An operand of essentially character type should not be used where an operand is interpreted as a numeric value An operand of essentially enum type should not be used in an arithmetic operation Shift and bitwise operations should not be performed on operands of essentially signed or enum type An operand of essentially signed or enum type should not be used as the right hand operand to the bitwise shifting operator An operand of essentially unsigned type should not be used as the operand to the unary minus operator The value of an expression shall not be assigned to an object with a narrower essential type The value of an expression shall not be assigned to an object of a different essential type category Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category The second and third operands of the ternary operator shall have the same essential type category The value of a composite expression shall not be assigned to an object with wider essential type If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential type If a composite expression is used as one (second or third) operand of a conditional operator then the other operand shall not have wider essential type Avoid integer overflows |
Polyspace Bug Finder | R2022b | CERT C: Rule INT31-C | Checks for: Integer conversion overflownteger conversion overflow, call to memset with unintended value all to memset with unintended value , sign change integer conversion overflowign change integer conversion overflow, tainted sign change conversionainted sign change conversion, unsigned integer conversion overflownsigned integer conversion overflow. Rule partially covered. |
PRQA QA-C | 9.7 | 2850, 2851, 2852, 2853, 2855, 2856, 2857, 2858, 2890, 2891, 2892, 2893, 2895, 2896, 2897, 2898 2900, 2901, 2902, 2903, 2905, 2906, 2907, 2908 | Partially implemented |
PRQA QA-C++ | 4.4 | 2850, 2851, 2852, 2853, 2855, 2856, 2857, 2858, 2890, 2891, 2892, 2893, 2895, 2896, 2897, 2898, 2900, 2901, 2902, 2903, 2905, 2906, 2907, 2908, 3000, 3010 | |
PVS-Studio | 7.23 | V562 , V569 , V642 , V676 , V716 , V721 , V724 , V732 , V739 , V784 , V793 , V1019 , V1029 , V1046 | |
RuleChecker | 22.04 | | Supported via MISRA C:2012 Rules 10.1, 10.3, 10.4, 10.6 and 10.7 |
TrustInSoft Analyzer | 1.38 | signed_downcast | Exhaustively verified. |
+\* Coverity Prevent cannot discover all violations of this rule, so further [verification](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-verification) is necessary.
+
+
+## Related Vulnerabilities
+
+[CVE-2009-1376](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1376) results from a violation of this rule. In version 2.5.5 of Pidgin, a `size_t` offset is set to the value of a 64-bit unsigned integer, which can lead to truncation \[[xorl 2009](http://xorl.wordpress.com/2009/05/28/cve-2009-1376-pidgin-msn-slp-integer-truncation/)\] on platforms where a `size_t` is implemented as a 32-bit unsigned integer. An attacker can execute arbitrary code by carefully choosing this value and causing a buffer overflow.
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerabi) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT31-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-195 and INT31-C**
+
+CWE-195 = Subset( CWE-192)
+
+INT31-C = Union( CWE-195, list) where list =
+
+* Unsigned-to-signed conversion error
+* Truncation that does not change sign
+**CWE-197 and INT31-C**
+
+See CWE-197 and FLP34-C
+
+**CWE-194 and INT31-C**
+
+CWE-194 = Subset( CWE-192)
+
+INT31-C = Union( CWE-194, list) where list =
+
+* Integer conversion that truncates significant data, but without loss of sign
+**CWE-20 and INT31-C**
+
+See CWE-20 and ERR34-C
+
+**CWE-704 and INT31-C**
+
+CWE-704 = Union( INT31-C, list) where list =
+
+* Improper type casts where either the source or target type is not an integral type
+**CWE-681 and INT31-C**
+
+CWE-681 = Union( INT31-C, FLP34-C)
+
+Intersection( INT31-C, FLP34-C) = Ø
+
+## Bibliography
+
+ \[ Dowd 2006 \] | Chapter 6, "C Language Issues" ("Type Conversions," pp. 223–270) |
\[ ISO/IEC 9899:2011 \] | 6.3.1.3, "Signed and Unsigned Integers" |
\[ Jones 2008 \] | Section 6.2.6.2, "Integer Types" |
\[ Seacord 2013b \] | Chapter 5, "Integer Security" |
\[ Viega 2005 \] | Section 5.2.9, "Truncation Error" Section 5.2.10, "Sign Extension Error" Section 5.2.11, "Signed to Unsigned Conversion Error" Section 5.2.12, "Unsigned to Signed Conversion Error" |
\[ Warren 2002 \] | Chapter 2, "Basics" |
\[ xorl 2009 \] | "CVE-2009-1376: Pidgin MSN SLP Integer Truncation" |
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.ql b/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.ql
new file mode 100644
index 0000000000..203e60a9e3
--- /dev/null
+++ b/c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.ql
@@ -0,0 +1,112 @@
+/**
+ * @id c/cert/integer-conversion-causes-data-loss
+ * @name INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data
+ * @description Converting an integer value to another integer type with a different sign or size
+ * can lead to data loss or misinterpretation of the value.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/int31-c
+ * correctness
+ * external/cert/severity/high
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+
+class IntegerConversion extends Expr {
+ private IntegralType castedToType;
+ private Expr preConversionExpr;
+
+ IntegerConversion() {
+ // This is an explicit cast
+ castedToType = this.(Cast).getType().getUnspecifiedType() and
+ preConversionExpr = this.(Cast).getExpr()
+ or
+ // Functions that internally cast an argument to unsigned char
+ castedToType instanceof UnsignedCharType and
+ this = preConversionExpr and
+ exists(FunctionCall call, string name | call.getTarget().hasGlobalOrStdName(name) |
+ name = ["ungetc", "fputc"] and
+ this = call.getArgument(0)
+ or
+ name = ["memset", "memchr"] and
+ this = call.getArgument(1)
+ or
+ name = "memset_s" and
+ this = call.getArgument(2)
+ )
+ }
+
+ Expr getPreConversionExpr() { result = preConversionExpr }
+
+ Type getCastedToType() { result = castedToType }
+}
+
+bindingset[value]
+predicate withinIntegralRange(IntegralType typ, float value) {
+ exists(float lb, float ub, float limit |
+ limit = 2.pow(8 * typ.getSize()) and
+ (
+ if typ.isUnsigned()
+ then (
+ lb = 0 and ub = limit - 1
+ ) else (
+ lb = -limit / 2 and
+ ub = (limit / 2) - 1
+ )
+ ) and
+ value >= lb and
+ value <= ub
+ )
+}
+
+from
+ IntegerConversion c, Expr preConversionExpr, Type castedToType, Type castedFromType,
+ IntegralType unspecifiedCastedFromType, string typeFromMessage, float preConversionLowerBound,
+ float preConversionUpperBound, float typeLowerBound, float typeUpperBound
+where
+ not isExcluded(c, IntegerOverflowPackage::integerConversionCausesDataLossQuery()) and
+ preConversionExpr = c.getPreConversionExpr() and
+ castedFromType = preConversionExpr.getType() and
+ // Casting from an integral type
+ unspecifiedCastedFromType = castedFromType.getUnspecifiedType() and
+ // Casting to an integral type
+ castedToType = c.getCastedToType() and
+ // Get the upper/lower bound of the pre-conversion expression
+ preConversionLowerBound = lowerBound(preConversionExpr) and
+ preConversionUpperBound = upperBound(preConversionExpr) and
+ // Get the upper/lower bound of the target type
+ typeLowerBound = typeLowerBound(castedToType) and
+ typeUpperBound = typeUpperBound(castedToType) and
+ // Where the result is not within the range of the target type
+ (
+ not withinIntegralRange(castedToType, preConversionLowerBound) or
+ not withinIntegralRange(castedToType, preConversionUpperBound)
+ ) and
+ // A conversion of `-1` to `time_t` is permitted by the standard
+ not (
+ c.getType().getUnspecifiedType().hasName("time_t") and
+ preConversionExpr.getValue() = "-1"
+ ) and
+ // Conversion to unsigned char is permitted from the range [SCHAR_MIN..UCHAR_MAX], as those can
+ // legitimately represent characters
+ not (
+ c.getType().getUnspecifiedType() instanceof UnsignedCharType and
+ lowerBound(preConversionExpr) >= typeLowerBound(any(SignedCharType s)) and
+ upperBound(preConversionExpr) <= typeUpperBound(any(UnsignedCharType s))
+ ) and
+ not castedToType instanceof BoolType and
+ // Create a helpful message
+ if castedFromType = unspecifiedCastedFromType
+ then typeFromMessage = castedFromType.toString()
+ else typeFromMessage = castedFromType + " (" + unspecifiedCastedFromType + ")"
+select c,
+ "Conversion from " + typeFromMessage + " to " + castedToType +
+ " may cause data loss (casting from range " + preConversionLowerBound + "..." +
+ preConversionUpperBound + " to range " + typeLowerBound + "..." + typeUpperBound + ")."
diff --git a/c/cert/src/rules/INT32-C/SignedIntegerOverflow.md b/c/cert/src/rules/INT32-C/SignedIntegerOverflow.md
new file mode 100644
index 0000000000..50a9d01dcd
--- /dev/null
+++ b/c/cert/src/rules/INT32-C/SignedIntegerOverflow.md
@@ -0,0 +1,482 @@
+# INT32-C: Ensure that operations on signed integers do not result in overflow
+
+This query implements the CERT-C rule INT32-C:
+
+> Ensure that operations on signed integers do not result in overflow
+
+
+## Description
+
+Signed integer overflow is [undefined behavior 36](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Consequently, [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) have considerable latitude in how they deal with signed integer overflow. (See [MSC15-C. Do not depend on undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior).) An implementation that defines signed integer types as being modulo, for example, need not detect integer overflow. Implementations may also trap on signed arithmetic overflows, or simply assume that overflows will never happen and generate object code accordingly. It is also possible for the same conforming implementation to emit code that exhibits different behavior in different contexts. For example, an implementation may determine that a signed integer loop control variable declared in a local scope cannot overflow and may emit efficient code on the basis of that determination, while the same implementation may determine that a global variable used in a similar context will wrap.
+
+For these reasons, it is important to ensure that operations on signed integers do not result in overflow. Of particular importance are operations on signed integer values that originate from a [tainted source](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedsource) and are used as
+
+* Integer operands of any pointer arithmetic, including array indexing
+* The assignment expression for the declaration of a variable length array
+* The postfix expression preceding square brackets `[]` or the expression in square brackets `[]` of a subscripted designation of an element of an array object
+* Function arguments of type `size_t` or `rsize_t` (for example, an argument to a memory allocation function)
+Integer operations will overflow if the resulting value cannot be represented by the underlying representation of the integer. The following table indicates which operations can result in overflow.
+
+ Operator | Overflow | Operator | Overflow | Operator | Overflow | Operator | Overflow |
+ | Yes | -= | Yes | << | Yes | < | No |
- | Yes | \*= | Yes | >> | No | > | No |
\* | Yes | /= | Yes | & | No | >= | No |
/ | Yes | %= | Yes | | | No | <= | No |
% | Yes | <<= | Yes | ^ | No | == | No |
++ | Yes | >>= | No | ~ | No | != | No |
-- | Yes | &= | No | ! | No | && | No |
= | No | |= | No | unary + | No | || | No |
+= | Yes | ^= | No | unary - | Yes | ?: | No |
+The following sections examine specific operations that are susceptible to integer overflow. When operating on integer types with less precision than `int`, integer promotions are applied. The usual arithmetic conversions may also be applied to (implicitly) convert operands to equivalent types before arithmetic operations are performed. Programmers should understand integer conversion rules before trying to implement secure arithmetic operations. (See [INT02-C. Understand integer conversion rules](https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules).)
+
+
+## Implementation Details
+
+GNU GCC invoked with the `[-fwrapv](http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Code-Gen-Options.html#index-fwrapv-2088)` command-line option defines the same modulo arithmetic for both unsigned and signed integers.
+
+GNU GCC invoked with the `[-ftrapv](http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Code-Gen-Options.html#index-ftrapv-2088)` command-line option causes a trap to be generated when a signed integer overflows, which will most likely abnormally exit. On a UNIX system, the result of such an event may be a signal sent to the process.
+
+GNU GCC invoked without either the `-fwrapv` or the `-ftrapv` option may simply assume that signed integers never overflow and may generate object code accordingly.
+
+## Atomic Integers
+
+The C Standard defines the behavior of arithmetic on atomic signed integer types to use two's complement representation with silent wraparound on overflow; there are no undefined results. Although defined, these results may be unexpected and therefore carry similar risks to [unsigned integer wrapping](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unsignedintegerwrapping). (See [INT30-C. Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap).) Consequently, signed integer overflow of atomic integer types should also be prevented or detected.
+
+## Addition
+
+Addition is between two operands of arithmetic type or between a pointer to an object type and an integer type. This rule applies only to addition between two operands of arithmetic type. (See [ARR37-C. Do not add or subtract an integer to a pointer to a non-array object](https://wiki.sei.cmu.edu/confluence/display/c/ARR37-C.+Do+not+add+or+subtract+an+integer+to+a+pointer+to+a+non-array+object) and [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts).)
+
+Incrementing is equivalent to adding 1.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in a signed integer overflow during the addition of the signed operands `si_a` and `si_b`:
+
+```cpp
+void func(signed int si_a, signed int si_b) {
+ signed int sum = si_a + si_b;
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution ensures that the addition operation cannot overflow, regardless of representation:
+
+```cpp
+#include
+
+void f(signed int si_a, signed int si_b) {
+ signed int sum;
+ if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
+ ((si_b < 0) && (si_a < (INT_MIN - si_b)))) {
+ /* Handle error */
+ } else {
+ sum = si_a + si_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution (GNU)**
+
+This compliant solution uses the GNU extension `__builtin_sadd_overflow`, available with GCC, Clang, and ICC:
+
+```cpp
+void f(signed int si_a, signed int si_b) {
+ signed int sum;
+ if (__builtin_sadd_overflow(si_a, si_b, &sum)) {
+ /* Handle error */
+ }
+ /* ... */
+}
+```
+
+## Subtraction
+
+Subtraction is between two operands of arithmetic type, two pointers to qualified or unqualified versions of compatible object types, or a pointer to an object type and an integer type. This rule applies only to subtraction between two operands of arithmetic type. (See [ARR36-C. Do not subtract or compare two pointers that do not refer to the same array](https://wiki.sei.cmu.edu/confluence/display/c/ARR36-C.+Do+not+subtract+or+compare+two+pointers+that+do+not+refer+to+the+same+array), [ARR37-C. Do not add or subtract an integer to a pointer to a non-array object](https://wiki.sei.cmu.edu/confluence/display/c/ARR37-C.+Do+not+add+or+subtract+an+integer+to+a+pointer+to+a+non-array+object), and [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts) for information about pointer subtraction.)
+
+Decrementing is equivalent to subtracting 1.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in a signed integer overflow during the subtraction of the signed operands `si_a` and `si_b`:
+
+```cpp
+void func(signed int si_a, signed int si_b) {
+ signed int diff = si_a - si_b;
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution tests the operands of the subtraction to guarantee there is no possibility of signed overflow, regardless of representation:
+
+```cpp
+#include
+
+void func(signed int si_a, signed int si_b) {
+ signed int diff;
+ if ((si_b > 0 && si_a < INT_MIN + si_b) ||
+ (si_b < 0 && si_a > INT_MAX + si_b)) {
+ /* Handle error */
+ } else {
+ diff = si_a - si_b;
+ }
+
+ /* ... */
+}
+```
+**Compliant Solution (GNU)**
+
+This compliant solution uses the GNU extension `__builtin_ssub_overflow`, available with GCC, Clang, and ICC:
+
+```cpp
+void func(signed int si_a, signed int si_b) {
+ signed int diff;
+ if (__builtin_ssub_overflow(si_a, si_b, &diff)) {
+ /* Handle error */
+ }
+
+ /* ... */
+}
+```
+
+## Multiplication
+
+Multiplication is between two operands of arithmetic type.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in a signed integer overflow during the multiplication of the signed operands `si_a` and `si_b`:
+
+```cpp
+void func(signed int si_a, signed int si_b) {
+ signed int result = si_a * si_b;
+ /* ... */
+}
+```
+**Compliant Solution**
+
+The product of two operands can always be represented using twice the number of bits than exist in the precision of the larger of the two operands. This compliant solution eliminates signed overflow on systems where `long long` is at least twice the precision of `int`:
+
+```cpp
+#include
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(umax_value) popcount(umax_value)
+
+void func(signed int si_a, signed int si_b) {
+ signed int result;
+ signed long long tmp;
+ assert(PRECISION(ULLONG_MAX) >= 2 * PRECISION(UINT_MAX));
+ tmp = (signed long long)si_a * (signed long long)si_b;
+
+ /*
+ * If the product cannot be represented as a 32-bit integer,
+ * handle as an error condition.
+ */
+ if ((tmp > INT_MAX) || (tmp < INT_MIN)) {
+ /* Handle error */
+ } else {
+ result = (int)tmp;
+ }
+ /* ... */
+}
+```
+The assertion fails if `long long` has less than twice the precision of `int`. The `PRECISION()` macro and `popcount()` function provide the correct precision for any integer type. (See [INT35-C. Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions).)
+
+**Compliant Solution**
+
+The following portable compliant solution can be used with any conforming implementation, including those that do not have an integer type that is at least twice the precision of `int`:
+
+```cpp
+#include
+
+void func(signed int si_a, signed int si_b) {
+ signed int result;
+ if (si_a > 0) { /* si_a is positive */
+ if (si_b > 0) { /* si_a and si_b are positive */
+ if (si_a > (INT_MAX / si_b)) {
+ /* Handle error */
+ }
+ } else { /* si_a positive, si_b nonpositive */
+ if (si_b < (INT_MIN / si_a)) {
+ /* Handle error */
+ }
+ } /* si_a positive, si_b nonpositive */
+ } else { /* si_a is nonpositive */
+ if (si_b > 0) { /* si_a is nonpositive, si_b is positive */
+ if (si_a < (INT_MIN / si_b)) {
+ /* Handle error */
+ }
+ } else { /* si_a and si_b are nonpositive */
+ if ( (si_a != 0) && (si_b < (INT_MAX / si_a))) {
+ /* Handle error */
+ }
+ } /* End if si_a and si_b are nonpositive */
+ } /* End if si_a is nonpositive */
+
+ result = si_a * si_b;
+}
+```
+**Compliant Solution (GNU)**
+
+This compliant solution uses the GNU extension `__builtin_smul_overflow`, available with GCC, Clang, and ICC:
+
+```cpp
+void func(signed int si_a, signed int si_b) {
+ signed int result;
+ if (__builtin_smul_overflow(si_a, si_b, &result)) {
+ /* Handle error */
+ }
+}
+```
+
+## Division
+
+Division is between two operands of arithmetic type. Overflow can occur during two's complement signed integer division when the dividend is equal to the minimum (negative) value for the signed integer type and the divisor is equal to `−1`. Division operations are also susceptible to divide-by-zero errors. (See [INT33-C. Ensure that division and remainder operations do not result in divide-by-zero errors](https://wiki.sei.cmu.edu/confluence/display/c/INT33-C.+Ensure+that+division+and+remainder+operations+do+not+result+in+divide-by-zero+errors).)
+
+**Noncompliant Code Example**
+
+This noncompliant code example prevents divide-by-zero errors in compliance with [INT33-C. Ensure that division and remainder operations do not result in divide-by-zero errors](https://wiki.sei.cmu.edu/confluence/display/c/INT33-C.+Ensure+that+division+and+remainder+operations+do+not+result+in+divide-by-zero+errors) but does not prevent a signed integer overflow error in two's-complement.
+
+```cpp
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if (s_b == 0) {
+ /* Handle error */
+ } else {
+ result = s_a / s_b;
+ }
+ /* ... */
+}
+```
+**Implementation Details**
+
+On the x86-32 architecture, overflow results in a fault, which can be exploited as a [denial-of-service attack](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service).
+
+**Compliant Solution**
+
+This compliant solution eliminates the possibility of divide-by-zero errors or signed overflow:
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_b == 0) || ((s_a == LONG_MIN) && (s_b == -1))) {
+ /* Handle error */
+ } else {
+ result = s_a / s_b;
+ }
+ /* ... */
+}
+```
+
+## Remainder
+
+The remainder operator provides the remainder when two operands of integer type are divided. Because many platforms implement remainder and division in the same instruction, the remainder operator is also susceptible to arithmetic overflow and division by zero. (See [INT33-C. Ensure that division and remainder operations do not result in divide-by-zero errors](https://wiki.sei.cmu.edu/confluence/display/c/INT33-C.+Ensure+that+division+and+remainder+operations+do+not+result+in+divide-by-zero+errors).)
+
+**Noncompliant Code Example**
+
+Many hardware architectures implement remainder as part of the division operator, which can overflow. Overflow can occur during a remainder operation when the dividend is equal to the minimum (negative) value for the signed integer type and the divisor is equal to −1. It occurs even though the result of such a remainder operation is mathematically 0. This noncompliant code example prevents divide-by-zero errors in compliance with [INT33-C. Ensure that division and remainder operations do not result in divide-by-zero errors](https://wiki.sei.cmu.edu/confluence/display/c/INT33-C.+Ensure+that+division+and+remainder+operations+do+not+result+in+divide-by-zero+errors) but does not prevent integer overflow:
+
+```cpp
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if (s_b == 0) {
+ /* Handle error */
+ } else {
+ result = s_a % s_b;
+ }
+ /* ... */
+}
+```
+**Implementation Details**
+
+On x86-32 platforms, the remainder operator for signed integers is implemented by the `idiv` instruction code, along with the divide operator. Because `LONG_MIN / −1` overflows, it results in a software exception with `LONG_MIN % −1` as well.
+
+**Compliant Solution**
+
+This compliant solution also tests the remainder operands to guarantee there is no possibility of an overflow:
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_b == 0 ) || ((s_a == LONG_MIN) && (s_b == -1))) {
+ /* Handle error */
+ } else {
+ result = s_a % s_b;
+ }
+ /* ... */
+}
+```
+
+## Left-Shift Operator
+
+The left-shift operator takes two integer operands. The result of `E1 << E2` is `E1` left-shifted `E2` bit positions; vacated bits are filled with zeros.
+
+The C Standard, 6.5.7, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
+
+> If `E1` has a signed type and nonnegative value, and `E1 × 2E2` is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
+
+
+In almost every case, an attempt to shift by a negative number of bits or by more bits than exist in the operand indicates a logic error. These issues are covered by [INT34-C. Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand](https://wiki.sei.cmu.edu/confluence/display/c/INT34-C.+Do+not+shift+an+expression+by+a+negative+number+of+bits+or+by+greater+than+or+equal+to+the+number+of+bits+that+exist+in+the+operand).
+
+**Noncompliant Code Example**
+
+This noncompliant code example performs a left shift, after verifying that the number being shifted is not negative, and the number of bits to shift is valid. The `PRECISION()` macro and `popcount()` function provide the correct precision for any integer type. (See [INT35-C. Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions).) However, because this code does no overflow check, it can result in an unrepresentable value.
+
+```cpp
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(umax_value) popcount(umax_value)
+
+void func(signed long si_a, signed long si_b) {
+ signed long result;
+ if ((si_a < 0) || (si_b < 0) ||
+ (si_b >= PRECISION(ULONG_MAX)) {
+ /* Handle error */
+ } else {
+ result = si_a << si_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution eliminates the possibility of overflow resulting from a left-shift operation:
+
+```cpp
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(umax_value) popcount(umax_value)
+
+void func(signed long si_a, signed long si_b) {
+ signed long result;
+ if ((si_a < 0) || (si_b < 0) ||
+ (si_b >= PRECISION(ULONG_MAX)) ||
+ (si_a > (LONG_MAX >> si_b))) {
+ /* Handle error */
+ } else {
+ result = si_a << si_b;
+ }
+ /* ... */
+}
+```
+
+## Unary Negation
+
+The unary negation operator takes an operand of arithmetic type. Overflow can occur during two's complement unary negation when the operand is equal to the minimum (negative) value for the signed integer type.
+
+**Noncompliant Code Example**
+
+This noncompliant code example can result in a signed integer overflow during the unary negation of the signed operand `s_a`:
+
+```cpp
+void func(signed long s_a) {
+ signed long result = -s_a;
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution tests the negation operation to guarantee there is no possibility of signed overflow:
+
+```cpp
+#include
+
+void func(signed long s_a) {
+ signed long result;
+ if (s_a == LONG_MIN) {
+ /* Handle error */
+ } else {
+ result = -s_a;
+ }
+ /* ... */
+}
+
+```
+
+## Risk Assessment
+
+Integer overflow can lead to buffer overflows and the execution of arbitrary code by an attacker.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT32-C | High | Likely | High | P9 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | integer-overflow | Fully checked |
CodeSonar | 7.2p0 | ALLOC.SIZE.ADDOFLOW ALLOC.SIZE.IOFLOW ALLOC.SIZE.MULOFLOW ALLOC.SIZE.SUBUFLOW MISC.MEM.SIZE.ADDOFLOW MISC.MEM.SIZE.BAD MISC.MEM.SIZE.MULOFLOW MISC.MEM.SIZE.SUBUFLOW | Addition overflow of allocation size Integer overflow of allocation size Multiplication overflow of allocation size Subtraction underflow of allocation size Addition overflow of size Unreasonable size argument Multiplication overflow of size Subtraction underflow of size |
Coverity | 2017.07 | TAINTED_SCALAR BAD_SHIFT | Implemented |
Helix QAC | 2022.4 | C2800, C2860 C++2800, C++2860 DF2801, DF2802, DF2803, DF2861, DF2862, DF2863 | |
Klocwork | 2022.4 | NUM.OVERFLOW CWARN.NOEFFECT.OUTOFRANGE NUM.OVERFLOW.DF | |
LDRA tool suite | 9.7.1 | 493 S, 494 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT32-a CERT_C-INT32-b CERT_C-INT32-c | Avoid integer overflows Integer overflow or underflow in constant expression in '+', '-', '\*' operator Integer overflow or underflow in constant expression in '<<' operator |
Parasoft Insure++ | | | Runtime analysis |
Polyspace Bug Finder | R2022b | CERT C: Rule INT32-C | Checks for: Integer overflownteger overflow, tainted division operandainted division operand, tainted modulo operandainted modulo operand. Rule partially covered. |
PRQA QA-C | 9.7 | 2800, 2801, 2802, 2803, 2860, 2861, 2862, 2863 | Fully implemented |
PRQA QA-C++ | 4.4 | 2800, 2801, 2802, 2803, 2860, 2861, 2862, 2863 | |
PVS-Studio | 7.23 | V1026, V1070, V1081, V1083, V1085, V5010 | |
TrustInSoft Analyzer | 1.38 | signed_overflow | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT32-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-20 and INT32-C**
+
+See CWE-20 and ERR34-C
+
+**CWE-680 and INT32-C**
+
+Intersection( INT32-C, MEM35-C) = Ø
+
+Intersection( CWE-680, INT32-C) =
+
+* Signed integer overflows that lead to buffer overflows
+CWE-680 - INT32-C =
+* Unsigned integer overflows that lead to buffer overflows
+INT32-C – CWE-680 =
+* Signed integer overflows that do not lead to buffer overflows
+**CWE-191 and INT32-C**
+
+Union( CWE-190, CWE-191) = Union( INT30-C, INT32-C)
+
+Intersection( INT30-C, INT32-C) == Ø
+
+Intersection(CWE-191, INT32-C) =
+
+* Underflow of signed integer operation
+CWE-191 – INT32-C =
+* Underflow of unsigned integer operation
+INT32-C – CWE-191 =
+* Overflow of signed integer operation
+**CWE-190 and INT32-C**
+
+Union( CWE-190, CWE-191) = Union( INT30-C, INT32-C)
+
+Intersection( INT30-C, INT32-C) == Ø
+
+Intersection(CWE-190, INT32-C) =
+
+* Overflow (wraparound) of signed integer operation
+CWE-190 – INT32-C =
+* Overflow of unsigned integer operation
+INT32-C – CWE-190 =
+* Underflow of signed integer operation
+
+## Bibliography
+
+ \[ Dowd 2006 \] | Chapter 6, "C Language Issues" ("Arithmetic Boundary Conditions," pp. 211–223) |
\[ ISO/IEC 9899:2011 \] | Subclause 6.5.5, "Multiplicative Operators" |
\[ Seacord 2013b \] | Chapter 5, "Integer Security" |
\[ Viega 2005 \] | Section 5.2.7, "Integer Overflow" |
\[ Warren 2002 \] | Chapter 2, "Basics" |
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT32-C: Ensure that operations on signed integers do not result in overflow](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT32-C/SignedIntegerOverflow.ql b/c/cert/src/rules/INT32-C/SignedIntegerOverflow.ql
new file mode 100644
index 0000000000..2edee2e5c6
--- /dev/null
+++ b/c/cert/src/rules/INT32-C/SignedIntegerOverflow.ql
@@ -0,0 +1,42 @@
+/**
+ * @id c/cert/signed-integer-overflow
+ * @name INT32-C: Ensure that operations on signed integers do not result in overflow
+ * @description The multiplication of two signed integers can lead to underflow or overflow and
+ * therefore undefined behavior.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/int32-c
+ * correctness
+ * security
+ * external/cert/severity/high
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p9
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.Overflow
+import semmle.code.cpp.controlflow.Guards
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+from InterestingOverflowingOperation op
+where
+ not isExcluded(op, IntegerOverflowPackage::signedIntegerOverflowQuery()) and
+ (
+ // An operation that returns a signed integer type
+ op.getType().getUnderlyingType().(IntegralType).isSigned()
+ or
+ // The divide or rem expression on a signed integer
+ op.(DivOrRemOperation).getDividend().getType().getUnderlyingType().(IntegralType).isSigned()
+ ) and
+ // Not checked before the operation
+ not op.hasValidPreCheck() and
+ // Covered by INT34-C
+ not op instanceof LShiftExpr
+select op,
+ "Operation " + op.getOperator() + " of type " + op.getType().getUnderlyingType() +
+ " may overflow or underflow."
diff --git a/c/cert/src/rules/INT33-C/DivOrRemByZero.md b/c/cert/src/rules/INT33-C/DivOrRemByZero.md
new file mode 100644
index 0000000000..0810ee078c
--- /dev/null
+++ b/c/cert/src/rules/INT33-C/DivOrRemByZero.md
@@ -0,0 +1,138 @@
+# INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors
+
+This query implements the CERT-C rule INT33-C:
+
+> Ensure that division and remainder operations do not result in divide-by-zero errors
+
+
+## Description
+
+The C Standard identifies the following condition under which division and remainder operations result in [undefined behavior (UB)](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
+
+ UB | Description |
45 | The value of the second operand of the / or % operator is zero (6.5.5). |
+Ensure that division and remainder operations do not result in divide-by-zero errors.
+
+
+## Division
+
+The result of the `/` operator is the quotient from the division of the first arithmetic operand by the second arithmetic operand. Division operations are susceptible to divide-by-zero errors. Overflow can also occur during two's complement signed integer division when the dividend is equal to the minimum (most negative) value for the signed integer type and the divisor is equal to `−1.` (See [INT32-C. Ensure that operations on signed integers do not result in overflow](https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow).)
+
+**Noncompliant Code Example**
+
+This noncompliant code example prevents signed integer overflow in compliance with [INT32-C. Ensure that operations on signed integers do not result in overflow](https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow) but fails to prevent a divide-by-zero error during the division of the signed operands `s_a` and `s_b`:` `
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_a == LONG_MIN) && (s_b == -1)) {
+ /* Handle error */
+ } else {
+ result = s_a / s_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution tests the division operation to guarantee there is no possibility of divide-by-zero errors or signed overflow:
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_b == 0) || ((s_a == LONG_MIN) && (s_b == -1))) {
+ /* Handle error */
+ } else {
+ result = s_a / s_b;
+ }
+ /* ... */
+}
+```
+
+## Remainder
+
+The remainder operator provides the remainder when two operands of integer type are divided.
+
+**Noncompliant Code Example**
+
+This noncompliant code example prevents signed integer overflow in compliance with [INT32-C. Ensure that operations on signed integers do not result in overflow](https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow) but fails to prevent a divide-by-zero error during the remainder operation on the signed operands `s_a` and `s_b`:
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_a == LONG_MIN) && (s_b == -1)) {
+ /* Handle error */
+ } else {
+ result = s_a % s_b;
+ }
+ /* ... */
+}
+```
+**Compliant Solution**
+
+This compliant solution tests the remainder operand to guarantee there is no possibility of a divide-by-zero error or an overflow error:
+
+```cpp
+#include
+
+void func(signed long s_a, signed long s_b) {
+ signed long result;
+ if ((s_b == 0 ) || ((s_a == LONG_MIN) && (s_b == -1))) {
+ /* Handle error */
+ } else {
+ result = s_a % s_b;
+ }
+ /* ... */
+}
+```
+
+## Risk Assessment
+
+A divide-by-zero error can result in [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination) and denial of service.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT33-C | Low | Likely | Medium | P6 | L2 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | int-division-by-zero int-modulo-by-zero | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-INT33 | |
CodeSonar | 7.2p0 | LANG.ARITH.DIVZEROLANG.ARITH.FDIVZERO | Division by zero Float Division By Zero |
Compass/ROSE | | | Can detect some violations of this rule (In particular, it ensures that all operations involving division or modulo are preceded by a check ensuring that the second operand is nonzero.) |
Coverity | 2017.07 | DIVIDE_BY_ZERO | Fully implemented |
Cppcheck | 1.66 | zerodivzerodivcond | Context sensitive analysis of division by zero Not detected for division by struct member / array element / pointer data that is 0 Detected when there is unsafe division by variable before/after test if variable is zero |
Helix QAC | 2022.4 | C2830 C++2830 DF2831, DF2832, DF2833 | |
Klocwork | 2022.4 | DBZ.CONST DBZ.CONST.CALL DBZ.GENERAL DBZ.ITERATOR DBZ.ITERATOR.CALL | |
LDRA tool suite | 9.7.1 | 43 D, 127 D, 248 S, 629 S, 80 X | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT33-a | Avoid division by zero |
Parasoft Insure++ | | | Runtime analysis |
Polyspace Bug Finder | R2022b | CERT C: Rule INT33-C | Checks for: Integer division by zeronteger division by zero, tainted division operandainted division operand, tainted modulo operandainted modulo operand. Rule fully covered. |
PRQA QA-C | 9.7 | 2830 \[C\], 2831 \[D\], 2832 \[A\] 2833 \[S\] | Fully implemented |
PRQA QA-C++ | 4.4 | 2831, 2832, 2833 | |
SonarQube C/C++ Plugin | 3.11 | S3518 | |
PVS-Studio | 7.23 | V609 | |
TrustInSoft Analyzer | 1.38 | division_by_zero | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT33-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-682 and INT33-C**
+
+CWE-682 = Union( INT33-C, list) where list =
+
+* Incorrect calculations that do not involve division by zero
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT33-C/DivOrRemByZero.ql b/c/cert/src/rules/INT33-C/DivOrRemByZero.ql
new file mode 100644
index 0000000000..6090e8842a
--- /dev/null
+++ b/c/cert/src/rules/INT33-C/DivOrRemByZero.ql
@@ -0,0 +1,42 @@
+/**
+ * @id c/cert/div-or-rem-by-zero
+ * @name INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors
+ * @description Dividing or taking the remainder by zero is undefined behavior.
+ * @kind problem
+ * @precision medium
+ * @problem.severity error
+ * @tags external/cert/id/int33-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/likely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p6
+ * external/cert/level/l2
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import codingstandards.cpp.Overflow
+import semmle.code.cpp.controlflow.Guards
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+from DivOrRemOperation divOrMod, Expr divisor
+where
+ not isExcluded(divOrMod, IntegerOverflowPackage::divOrRemByZeroQuery()) and
+ divisor = divOrMod.getDivisor() and
+ divisor.getType() instanceof IntegralType and
+ // Range includes 0
+ upperBound(divisor) >= 0 and
+ lowerBound(divisor) <= 0 and
+ // And an explicit check for 0 does not exist
+ not exists(GuardCondition gc, Expr left, Expr right |
+ gc.ensuresEq(left, right, 0, divOrMod.getBasicBlock(), false) and
+ globalValueNumber(left) = globalValueNumber(divisor) and
+ right.getValue().toInt() = 0
+ ) and
+ // Uninstantiated templates may not have an accurate reflection of the range
+ not divOrMod.getEnclosingFunction().isFromUninstantiatedTemplate(_)
+select divOrMod,
+ "Division or remainder expression with divisor that may be zero (divisor range " +
+ lowerBound(divisor) + "..." + upperBound(divisor) + ")."
diff --git a/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.md b/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.md
new file mode 100644
index 0000000000..0c9a635019
--- /dev/null
+++ b/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.md
@@ -0,0 +1,216 @@
+# INT34-C: Bit shift should not be done by a negative operand or an operand of greater-or-equal precision than that of another
+
+This query implements the CERT-C rule INT34-C:
+
+> Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand
+
+
+## Description
+
+Bitwise shifts include left-shift operations of the form *shift-expression* `<<` *additive-expression* and right-shift operations of the form *shift-expression* `>>` *additive-expression*. The standard integer promotions are first performed on the operands, each of which has an integer type. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 51](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_51).)
+
+Do not shift an expression by a negative number of bits or by a number greater than or equal to the *precision* of the promoted left operand. The precision of an integer type is the number of bits it uses to represent values, excluding any sign and padding bits. For unsigned integer types, the width and the precision are the same; whereas for signed integer types, the width is one greater than the precision. This rule uses precision instead of width because, in almost every case, an attempt to shift by a number of bits greater than or equal to the precision of the operand indicates a bug (logic error). A logic error is different from overflow, in which there is simply a representational deficiency. In general, shifts should be performed only on unsigned operands. (See [INT13-C. Use bitwise operators only on unsigned operands](https://wiki.sei.cmu.edu/confluence/display/c/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands).)
+
+## Noncompliant Code Example (Left Shift, Unsigned Type)
+
+The result of `E1 << E2` is `E1` left-shifted `E2` bit positions; vacated bits are filled with zeros. The following diagram illustrates the left-shift operation.
+
+
+
+According to the C Standard, if `E1` has an unsigned type, the value of the result is `E1` \* `2``E2`, reduced modulo 1 more than the maximum value representable in the result type.
+
+This noncompliant code example fails to ensure that the right operand is less than the precision of the promoted left operand:
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int uresult = ui_a << ui_b;
+ /* ... */
+}
+```
+
+## Compliant Solution (Left Shift, Unsigned Type)
+
+This compliant solution eliminates the possibility of shifting by greater than or equal to the number of bits that exist in the precision of the left operand:
+
+```cpp
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(x) popcount(x)
+
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int uresult = 0;
+ if (ui_b >= PRECISION(UINT_MAX)) {
+ /* Handle error */
+ } else {
+ uresult = ui_a << ui_b;
+ }
+ /* ... */
+}
+```
+The `PRECISION()` macro and `popcount()` function provide the correct precision for any integer type. (See [INT35-C. Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions).)
+
+Modulo behavior resulting from left-shifting an unsigned integer type is permitted by exception INT30-EX3 to [INT30-C. Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap).
+
+## Noncompliant Code Example (Left Shift, Signed Type)
+
+The result of `E1 << E2` is `E1` left-shifted `E2` bit positions; vacated bits are filled with zeros. If `E1` has a signed type and nonnegative value, and `E1` \* `2``E2` is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
+
+This noncompliant code example fails to ensure that left and right operands have nonnegative values and that the right operand is less than the precision of the promoted left operand. This example does check for signed integer overflow in compliance with [INT32-C. Ensure that operations on signed integers do not result in overflow](https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow).
+
+```cpp
+#include
+#include
+#include
+
+void func(signed long si_a, signed long si_b) {
+ signed long result;
+ if (si_a > (LONG_MAX >> si_b)) {
+ /* Handle error */
+ } else {
+ result = si_a << si_b;
+ }
+ /* ... */
+}
+```
+Shift operators and other bitwise operators should be used only with unsigned integer operands in accordance with [INT13-C. Use bitwise operators only on unsigned operands](https://wiki.sei.cmu.edu/confluence/display/c/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands).
+
+## Compliant Solution (Left Shift, Signed Type)
+
+In addition to the check for overflow, this compliant solution ensures that both the left and right operands have nonnegative values and that the right operand is less than the precision of the promoted left operand:
+
+```cpp
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(x) popcount(x)
+
+void func(signed long si_a, signed long si_b) {
+ signed long result;
+ if ((si_a < 0) || (si_b < 0) ||
+ (si_b >= PRECISION(ULONG_MAX)) ||
+ (si_a > (LONG_MAX >> si_b))) {
+ /* Handle error */
+ } else {
+ result = si_a << si_b;
+ }
+ /* ... */
+}
+
+```
+Noncompliant Code Example (Right Shift)
+
+The result of `E1 >> E2` is `E1` right-shifted `E2` bit positions. If `E1` has an unsigned type or if `E1` has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of `E1` / `2``E2`. If `E1` has a signed type and a negative value, the resulting value is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) and can be either an arithmetic (signed) shift
+
+
+
+or a logical (unsigned) shift
+
+
+
+This noncompliant code example fails to test whether the right operand is greater than or equal to the precision of the promoted left operand, allowing undefined behavior:
+
+```cpp
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int uresult = ui_a >> ui_b;
+ /* ... */
+}
+```
+When working with signed operands, making assumptions about whether a right shift is implemented as an arithmetic (signed) shift or a logical (unsigned) shift can also lead to [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability). (See [INT13-C. Use bitwise operators only on unsigned operands](https://wiki.sei.cmu.edu/confluence/display/c/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands).)
+
+## Compliant Solution (Right Shift)
+
+This compliant solution eliminates the possibility of shifting by greater than or equal to the number of bits that exist in the precision of the left operand:
+
+```cpp
+#include
+#include
+#include
+
+extern size_t popcount(uintmax_t);
+#define PRECISION(x) popcount(x)
+
+void func(unsigned int ui_a, unsigned int ui_b) {
+ unsigned int uresult = 0;
+ if (ui_b >= PRECISION(UINT_MAX)) {
+ /* Handle error */
+ } else {
+ uresult = ui_a >> ui_b;
+ }
+ /* ... */
+}
+```
+**Implementation Details**
+
+GCC has no options to handle shifts by negative amounts or by amounts outside the width of the type predictably or to trap on them; they are always treated as undefined. Processors may reduce the shift amount modulo the width of the type. For example, 32-bit right shifts are implemented using the following instruction on x86-32:
+
+```cpp
+sarl %cl, %eax
+
+```
+The `sarl` instruction takes a bit mask of the least significant 5 bits from `%cl` to produce a value in the range \[0, 31\] and then shift `%eax` that many bits:
+
+```cpp
+// 64-bit right shifts on IA-32 platforms become
+shrdl %edx, %eax
+sarl %cl, %edx
+
+```
+where `%eax` stores the least significant bits in the doubleword to be shifted, and `%edx` stores the most significant bits.
+
+## Risk Assessment
+
+Although shifting a negative number of bits or shifting a number of bits greater than or equal to the width of the promoted left operand is undefined behavior in C, the risk is generally low because processors frequently reduce the shift amount modulo the width of the type.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT34-C | Low | Unlikely | Medium | P2 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | precision-shift-width precision-shift-width-constant | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-INT34 | Can detect shifts by a negative or an excessive number of bits and right shifts on negative values. |
CodeSonar | 7.2p0 | LANG.ARITH.BIGSHIFT LANG.ARITH.NEGSHIFT | Shift amount exceeds bit width Negative shift amount |
Compass/ROSE | | | Can detect violations of this rule. Unsigned operands are detected when checking for INT13-C. Use bitwise operators only on unsigned operands |
Coverity | 2017.07 | BAD_SHIFT | Implemented |
Cppcheck | 1.66 | shiftNegative, shiftTooManyBits | Context sensitive analysis Warns whenever Cppcheck sees a negative shift for a POD expression (The warning for shifting too many bits is written only if Cppcheck has sufficient type information and you use --platform to specify the sizes of the standard types.) |
ECLAIR | 1.2 | CC2.INT34 | Partially implemented |
Helix QAC | 2022.4 | C0499, C2790, C++2790, C++3003 DF2791, DF2792, DF2793 | |
Klocwork | 2022.4 | MISRA.SHIFT.RANGE.2012 | |
LDRA tool suite | 9.7.1 | 51 S, 403 S, 479 S | Partially implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT34-a | Avoid incorrect shift operations |
Polyspace Bug Finder | R2022b | CERT C: Rule INT34-C | Checks for: Shift of a negative valuehift of a negative value, shift operation overflowhift operation overflow. Rule partially covered. |
PRQA QA-C | 9.7 | 0499, 2790 \[C\], 2791 \[D\], 2792 \[A\], 2793 \[S\] | Partially implemented |
PRQA QA-C++ | 4.4 | 2791, 2792, 2793, 3003, 3321, 3322 | |
PVS-Studio | 7.23 | V610 | |
RuleChecker | 22.04 | precision-shift-width-constant | Partially checked |
TrustInSoft Analyzer | 1.38 | shift | Exhaustively verified (see one compliant and one non-compliant example ). |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT34-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-758 and INT34-C**
+
+Independent( INT34-C, INT36-C, MEM30-C, MSC37-C, FLP32-C, EXP33-C, EXP30-C, ERR34-C, ARR32-C)
+
+CWE-758 = Union( INT34-C, list) where list =
+
+* Undefined behavior that results from anything other than incorrect bit shifting
+**CWE-682 and INT34-C**
+
+Independent( INT34-C, FLP32-C, INT33-C) CWE-682 = Union( INT34-C, list) where list =
+
+* Incorrect calculations that do not involve out-of-range bit shifts
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT34-C: Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.ql b/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.ql
new file mode 100644
index 0000000000..4260a5e677
--- /dev/null
+++ b/c/cert/src/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.ql
@@ -0,0 +1,27 @@
+/**
+ * @id c/cert/expr-shiftedby-negative-or-greater-precision-operand
+ * @name INT34-C: Bit shift should not be done by a negative operand or an operand of greater-or-equal precision than that of another
+ * @description Shifting an expression by an operand that is negative or of precision greater or
+ * equal to that or the another causes representational error.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/int34-c
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.controlflow.Guards
+import codingstandards.cpp.UndefinedBehavior
+
+from ShiftByNegativeOrGreaterPrecisionOperand badShift
+where not isExcluded(badShift, Types1Package::exprShiftedbyNegativeOrGreaterPrecisionOperandQuery())
+select badShift, badShift.getReason()
diff --git a/c/cert/src/rules/INT34-C/LogicalShiftRight.JPG b/c/cert/src/rules/INT34-C/LogicalShiftRight.JPG
new file mode 100644
index 0000000000..5f98215baf
Binary files /dev/null and b/c/cert/src/rules/INT34-C/LogicalShiftRight.JPG differ
diff --git a/c/cert/src/rules/INT34-C/ShiftLeft.JPG b/c/cert/src/rules/INT34-C/ShiftLeft.JPG
new file mode 100644
index 0000000000..c0134446ed
Binary files /dev/null and b/c/cert/src/rules/INT34-C/ShiftLeft.JPG differ
diff --git a/c/cert/src/rules/INT34-C/ShiftRight.JPG b/c/cert/src/rules/INT34-C/ShiftRight.JPG
new file mode 100644
index 0000000000..edbcf5ed61
Binary files /dev/null and b/c/cert/src/rules/INT34-C/ShiftRight.JPG differ
diff --git a/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.md b/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.md
new file mode 100644
index 0000000000..7cf3875831
--- /dev/null
+++ b/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.md
@@ -0,0 +1,142 @@
+# INT35-C: Use correct integer precisions
+
+This query implements the CERT-C rule INT35-C:
+
+> Use correct integer precisions
+
+
+## Description
+
+Integer types in C have both a *size* and a *precision*. The size indicates the number of bytes used by an object and can be retrieved for any object or type using the `sizeof` operator. The precision of an integer type is the number of bits it uses to represent values, excluding any sign and padding bits.
+
+Padding bits contribute to the integer's size, but not to its precision. Consequently, inferring the precision of an integer type from its size may result in too large a value, which can then lead to incorrect assumptions about the numeric range of these types. Programmers should use correct integer precisions in their code, and in particular, should not use the `sizeof` operator to compute the precision of an integer type on architectures that use padding bits or in strictly conforming (that is, portable) programs.
+
+## Noncompliant Code Example
+
+This noncompliant code example illustrates a function that produces 2 raised to the power of the function argument. To prevent undefined behavior in compliance with [INT34-C. Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand](https://wiki.sei.cmu.edu/confluence/display/c/INT34-C.+Do+not+shift+an+expression+by+a+negative+number+of+bits+or+by+greater+than+or+equal+to+the+number+of+bits+that+exist+in+the+operand), the function ensures that the argument is less than the number of bits used to store a value of type `unsigned int`.
+
+```cpp
+#include
+
+unsigned int pow2(unsigned int exp) {
+ if (exp >= sizeof(unsigned int) * CHAR_BIT) {
+ /* Handle error */
+ }
+ return 1 << exp;
+}
+```
+However, if this code runs on a platform where `unsigned int` has one or more padding bits, it can still result in values for `exp` that are too large. For example, on a platform that stores `unsigned int` in 64 bits, but uses only 48 bits to represent the value, a left shift of 56 bits would result in undefined behavior.
+
+## Compliant Solution
+
+This compliant solution uses a `popcount()` function, which counts the number of bits set on any unsigned integer, allowing this code to determine the precision of any integer type, signed or unsigned.
+
+```cpp
+#include
+#include
+
+/* Returns the number of set bits */
+size_t popcount(uintmax_t num) {
+ size_t precision = 0;
+ while (num != 0) {
+ if (num % 2 == 1) {
+ precision++;
+ }
+ num >>= 1;
+ }
+ return precision;
+}
+#define PRECISION(umax_value) popcount(umax_value)
+```
+Implementations can replace the `PRECISION()` macro with a type-generic macro that returns an integer constant expression that is the precision of the specified type for that implementation. This return value can then be used anywhere an integer constant expression can be used, such as in a static assertion. (See [DCL03-C. Use a static assertion to test the value of a constant expression](https://wiki.sei.cmu.edu/confluence/display/c/DCL03-C.+Use+a+static+assertion+to+test+the+value+of+a+constant+expression).) The following type generic macro, for example, might be used for a specific implementation targeting the IA-32 architecture:
+
+```cpp
+#define PRECISION(value) _Generic(value, \
+ unsigned char : 8, \
+ unsigned short: 16, \
+ unsigned int : 32, \
+ unsigned long : 32, \
+ unsigned long long : 64, \
+ signed char : 7, \
+ signed short : 15, \
+ signed int : 31, \
+ signed long : 31, \
+ signed long long : 63)
+```
+The revised version of the `pow2()` function uses the `PRECISION()` macro to determine the precision of the unsigned type:
+
+```cpp
+#include
+#include
+#include
+extern size_t popcount(uintmax_t);
+#define PRECISION(umax_value) popcount(umax_value)
+unsigned int pow2(unsigned int exp) {
+ if (exp >= PRECISION(UINT_MAX)) {
+ /* Handle error */
+ }
+ return 1 << exp;
+}
+```
+**Implementation Details**
+
+Some platforms, such as the Cray Linux Environment (CLE; supported on Cray XT CNL compute nodes), provide `a _popcnt` instruction that can substitute for the `popcount()` function.
+
+```cpp
+#define PRECISION(umax_value) _popcnt(umax_value)
+
+```
+
+## Risk Assessment
+
+Mistaking an integer's size for its precision can permit invalid precision arguments to operations such as bitwise shifts, resulting in undefined behavior.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT35-C | Low | Unlikely | Medium | P2 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | | Supported: Astrée reports overflows due to insufficient precision. |
CodeSonar | 7.2p0 | LANG.ARITH.BIGSHIFT | Shift Amount Exceeds Bit Width |
Helix QAC | 2022.4 | C0582 C++3115 | |
Parasoft C/C++test | 2022.2 | CERT_C-INT35-a | Use correct integer precisions when checking the right hand operand of the shift operator |
Polyspace Bug Finder | R2022b | CERT C: Rule INT35-C | Checks for situations when integer precisions are exceeded (rule fully covered) |
PRQA QA-C | 9.7 | 0582 | |
+
+
+##
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+ Taxonomy | Taxonomy item | Relationship |
CWE 2.11 | CWE-681 , Incorrect Conversion between Numeric Types | 2017-10-30:MITRE: Unspecified Relationship 2018-10-18:CERT:Partial Overlap |
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-190 and INT35-C**
+
+Intersection( INT35-C, CWE-190) = Ø
+
+INT35-C used to map to CWE-190 but has been replaced with a new rule that has no overlap with CWE-190.
+
+**CWE-681 and INT35-C**
+
+Intersection(INT35-C, CWE-681) = due to incorrect use of integer precision, conversion from one data type to another causing data to be omitted or translated in a way that produces unexpected values
+
+CWE-681 - INT35-C = list2, where list2 =
+
+* conversion from one data type to another causing data to be omitted or translated in a way that produces unexpected values, not involving incorrect use of integer precision
+INT35-C - CWE-681 = list1, where list1 =
+* incorrect use of integer precision not related to conversion from one data type to another
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT35-C: Use correct integer precisions](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.ql b/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.ql
new file mode 100644
index 0000000000..1bc372506d
--- /dev/null
+++ b/c/cert/src/rules/INT35-C/UseCorrectIntegerPrecisions.ql
@@ -0,0 +1,40 @@
+/**
+ * @id c/cert/use-correct-integer-precisions
+ * @name INT35-C: Use correct integer precisions
+ * @description The precision of integer types in C cannot be deduced from the size of the type (due
+ * to padding and sign bits) otherwise a loss of data may occur.
+ * @kind problem
+ * @precision high
+ * @problem.severity error
+ * @tags external/cert/id/int35-c
+ * correctness
+ * external/cert/severity/low
+ * external/cert/likelihood/unlikely
+ * external/cert/remediation-cost/medium
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+
+class CharBitMacroInvocation extends MacroInvocation {
+ CharBitMacroInvocation() { this.getMacroName() = "CHAR_BIT" }
+}
+
+from SizeofOperator so, RelationalOperation comparison, MulExpr m, Expr charSize
+where
+ not isExcluded(so, IntegerOverflowPackage::useCorrectIntegerPrecisionsQuery()) and
+ // Multiplication of a sizeof operator and a constant that's probably a char size
+ m.getAnOperand() = so and
+ m.getAnOperand() = charSize and
+ not so = charSize and
+ (
+ charSize.getValue().toInt() = 8
+ or
+ charSize = any(CharBitMacroInvocation c).getExpr()
+ ) and
+ // The result is compared against something, which is probably related to the number of bits
+ comparison.getAnOperand() = m
+select so, "sizeof operator used to determine the precision of an integer type."
diff --git a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.md b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.md
new file mode 100644
index 0000000000..1b4662ab74
--- /dev/null
+++ b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.md
@@ -0,0 +1,216 @@
+# INT36-C: Do not convert pointers to integers and back
+
+This query implements the CERT-C rule INT36-C:
+
+> Converting a pointer to integer or integer to pointer
+
+
+## Description
+
+Although programmers often use integers and pointers interchangeably in C, pointer-to-integer and integer-to-pointer conversions are [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior).
+
+Conversions between integers and pointers can have undesired consequences depending on the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation). According to the C Standard, subclause 6.3.2.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\],
+
+> An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
+
+
+> Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
+
+
+Do not convert an integer type to a pointer type if the resulting pointer is incorrectly aligned, does not point to an entity of the referenced type, or is a [trap representation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-traprepresentation).
+
+Do not convert a pointer type to an integer type if the result cannot be represented in the integer type. (See [undefined behavior 24](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_24).)
+
+The mapping between pointers and integers must be consistent with the addressing structure of the execution environment. Issues may arise, for example, on architectures that have a segmented memory model.
+
+## Noncompliant Code Example
+
+The size of a pointer can be greater than the size of an integer, such as in an implementation where pointers are 64 bits and unsigned integers are 32 bits. This code example is noncompliant on such implementations because the result of converting the 64-bit `ptr` cannot be represented in the 32-bit integer type:
+
+```cpp
+void f(void) {
+ char *ptr;
+ /* ... */
+ unsigned int number = (unsigned int)ptr;
+ /* ... */
+}
+
+```
+
+## Compliant Solution
+
+Any valid pointer to `void` can be converted to `intptr_t` or `uintptr_t` and back with no change in value. (See **INT36-EX2**.) The C Standard guarantees that a pointer to `void` may be converted to or from a pointer to any object type and back again and that the result must compare equal to the original pointer. Consequently, converting directly from a `char *` pointer to a `uintptr_t`, as in this compliant solution, is allowed on implementations that support the `uintptr_t` type.
+
+```cpp
+#include
+
+void f(void) {
+ char *ptr;
+ /* ... */
+ uintptr_t number = (uintptr_t)ptr;
+ /* ... */
+}
+
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, the pointer `ptr` is converted to an integer value. The high-order 9 bits of the number are used to hold a flag value, and the result is converted back into a pointer. This example is noncompliant on an implementation where pointers are 64 bits and unsigned integers are 32 bits because the result of converting the 64-bit `ptr` cannot be represented in the 32-bit integer type.
+
+```cpp
+void func(unsigned int flag) {
+ char *ptr;
+ /* ... */
+ unsigned int number = (unsigned int)ptr;
+ number = (number & 0x7fffff) | (flag << 23);
+ ptr = (char *)number;
+}
+
+```
+A similar scheme was used in early versions of Emacs, limiting its portability and preventing the ability to edit files larger than 8MB.
+
+## Compliant Solution
+
+This compliant solution uses a `struct` to provide storage for both the pointer and the flag value. This solution is portable to machines of different word sizes, both smaller and larger than 32 bits, working even when pointers cannot be represented in any integer type.
+
+```cpp
+struct ptrflag {
+ char *pointer;
+ unsigned int flag : 9;
+} ptrflag;
+
+void func(unsigned int flag) {
+ char *ptr;
+ /* ... */
+ ptrflag.pointer = ptr;
+ ptrflag.flag = flag;
+}
+
+```
+
+## Noncompliant Code Example
+
+It is sometimes necessary to access memory at a specific location, requiring a literal integer to pointer conversion. In this noncompliant code, a pointer is set directly to an integer constant, where it is unknown whether the result will be as intended:
+
+```cpp
+unsigned int *g(void) {
+ unsigned int *ptr = 0xdeadbeef;
+ /* ... */
+ return ptr;
+}
+```
+The result of this assignment is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior), might not be correctly aligned, might not point to an entity of the referenced type, and might be a [trap representation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-traprepresentation).
+
+## Compliant Solution
+
+Unfortunately this code cannot be made safe while strictly conforming to ISO C.
+
+A particular platform (that is, hardware, operating system, compiler, and Standard C library) might guarantee that a memory address is correctly aligned for the pointer type, and actually contains a value for that type. A common practice is to use addresses that are known to point to hardware that provides valid values.
+
+## Exceptions
+
+**INT36-C-EX1:** The integer value 0 can be converted to a pointer; it becomes the null pointer.
+
+**INT36-C-EX2:** Any valid pointer to `void` can be converted to `intptr_t` or `uintptr_t` or their underlying types and back again with no change in value. Use of underlying types instead of `intptr_t` or `uintptr_t` is discouraged, however, because it limits portability.
+
+```cpp
+#include
+#include
+
+void h(void) {
+ intptr_t i = (intptr_t)(void *)&i;
+ uintptr_t j = (uintptr_t)(void *)&j;
+
+ void *ip = (void *)i;
+ void *jp = (void *)j;
+
+ assert(ip == &i);
+ assert(jp == &j);
+}
+
+```
+
+## Risk Assessment
+
+Converting from pointer to integer or vice versa results in code that is not portable and may create unexpected pointers to invalid memory locations.
+
+ Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
INT36-C | Low | Probable | High | P2 | L3 |
+
+
+## Automated Detection
+
+ Tool | Version | Checker | Description |
Astrée | 22.04 | pointer-integral-cast pointer-integral-cast-implicit function-pointer-integer-cast function-pointer-integer-cast-implicit | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-INT36 | Fully implemented |
Clang | 3.9 | -Wint-to-pointer-cast , -Wint-conversion | Can detect some instances of this rule, but does not detect all |
CodeSonar | 7.2p0 | LANG.CAST.PC.CONST2PTRLANG.CAST.PC.INT | Conversion: integer constant to pointer Conversion: pointer/integer |
Compass/ROSE | | | |
Coverity | 2017.07 | PW.POINTER_CONVERSION_LOSES_BITS | Fully implemented |
Helix QAC | 2022.4 | C0303, C0305, C0306, C0309, C0324, C0326, C0360, C0361, C0362 C++3040, C++3041, C++3042, C++3043, C++3044, C++3045, C++3046, C++3047, C++3048 | |
Klocwork | 2022.4 | MISRA.CAST.OBJ_PTR_TO_INT.2012 | |
LDRA tool suite | 9.7.1 | 439 S, 440 S | Fully implemented |
Parasoft C/C++test | 2022.2 | CERT_C-INT36-b | A conversion should not be performed between a pointer to object type and an integer type other than 'uintptr_t' or 'intptr_t' |
PC-lint Plus | 1.4 | 4287 | Partially supported: reports casts from pointer types to smaller integer types which lose information |
Polyspace Bug Finder | R2022b | CERT C: Rule INT36-C | Checks for unsafe conversion between pointer and integer (rule partially covered) |
PRQA QA-C | 9.7 | 0303, 0305, 0306, 0309, 0324, 0326, 0360, 0361, 0362 | Partially implemented |
PRQA QA-C++ | 4.4 | 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048 | |
PVS-Studio | 7.23 | V527 , V528 , V542 , V566 , V601 , V647 , V1091 | |
RuleChecker | 22.04 | pointer-integral-cast pointer-integral-cast-implicit function-pointer-integer-cast function-pointer-integer-cast-implicit | Fully checked |
SonarQube C/C++ Plugin | 3.11 | S1767 | Partially implemented |
+
+
+## Related Vulnerabilities
+
+Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+INT36-C).
+
+## Related Guidelines
+
+[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
+
+
+
+
+## CERT-CWE Mapping Notes
+
+[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
+
+**CWE-758 and INT36-C**
+
+Independent( INT34-C, INT36-C, MEM30-C, MSC37-C, FLP32-C, EXP33-C, EXP30-C, ERR34-C, ARR32-C)
+
+CWE-758 = Union( INT36-C, list) where list =
+
+* Undefined behavior that results from anything other than integer <-> pointer conversion
+**CWE-704 and INT36-C**
+
+CWE-704 = Union( INT36-C, list) where list =
+
+* Incorrect (?) typecast that is not between integers and pointers
+**CWE-587 and INT36-C**
+
+Intersection( CWE-587, INT36-C) =
+
+* Setting a pointer to an integer value that is ill-defined (trap representation, improperly aligned, mis-typed, etc)
+CWE-587 – INT36-C =
+* Setting a pointer to a valid integer value (eg points to an object of the correct t ype)
+INT36-C – CWE-587 =
+* Illegal pointer-to-integer conversion
+Intersection(INT36-C,CWE-466) = ∅
+
+Intersection(INT36-C,CWE-466) = ∅
+
+An example explaining the above two equations follows:
+
+`static char x[3];`
+
+`char* foo() {`
+
+` int x_int = (int) x; // x_int = 999 eg`
+
+` return x_int + 5; // returns 1004 , violates CWE 466`
+
+`}`
+
+`...`
+
+`int y_int = foo(); // violates CWE-466`
+
+`char* y = (char*) y_int; // // well-defined but y may be invalid, violates INT36-C`
+
+`char c = *y; // indeterminate value, out-of-bounds read, violates CWE-119`
+
+## Bibliography
+
+
+
+
+## Implementation notes
+
+None
+
+## References
+
+* CERT-C: [INT36-C: Converting a pointer to integer or integer to pointer](https://wiki.sei.cmu.edu/confluence/display/c)
diff --git a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql
new file mode 100644
index 0000000000..1cbdcc4e12
--- /dev/null
+++ b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql
@@ -0,0 +1,76 @@
+/**
+ * @id c/cert/converting-a-pointer-to-integer-or-integer-to-pointer
+ * @name INT36-C: Do not convert pointers to integers and back
+ * @description Converting between pointers and integers is not portable and might cause invalid
+ * memory access.
+ * @kind problem
+ * @precision very-high
+ * @problem.severity error
+ * @tags external/cert/id/int36-c
+ * external/cert/severity/low
+ * external/cert/likelihood/probable
+ * external/cert/remediation-cost/high
+ * external/cert/priority/p2
+ * external/cert/level/l3
+ * external/cert/obligation/rule
+ */
+
+import cpp
+import codingstandards.c.cert
+
+class LiteralZero extends Literal {
+ LiteralZero() { this.getValue() = "0" }
+}
+
+class StdIntIntPtrType extends Type {
+ StdIntIntPtrType() {
+ exists(TypeDeclarationEntry entry |
+ /*
+ * Just check if there is a header file,
+ * because we don't know what header file the declaration might live in
+ */
+
+ exists(entry.getFile().(HeaderFile)) and
+ entry.getType() = this and
+ this.getName().regexpMatch("u?intptr_t")
+ )
+ }
+}
+
+/**
+ * Casting a pointer value to integer, excluding literal 0.
+ * Includes implicit conversions made during declarations or assignments.
+ */
+predicate conversionBetweenPointerAndInteger(Cast cast, string message) {
+ /* Ensure that `int` has different size than that of pointers */
+ exists(IntType intType, PointerType ptrType | intType.getSize() < ptrType.getSize() |
+ cast.getExpr().getUnderlyingType() = intType and
+ cast.getUnderlyingType() = ptrType and
+ if cast.isCompilerGenerated()
+ then message = "Integer expression " + cast.getExpr() + " is implicitly cast to a pointer type."
+ else message = "Integer expression " + cast.getExpr() + " is cast to a pointer type."
+ or
+ cast.getExpr().getUnderlyingType() = ptrType and
+ cast.getUnderlyingType() = intType and
+ if cast.isCompilerGenerated()
+ then
+ message = "Pointer expression " + cast.getExpr() + " is implicitly cast to an integer type."
+ else message = "Pointer expression " + cast.getExpr() + " is cast to an integer type."
+ ) and
+ /* Compliant exception 1: literal 0 */
+ not cast.getExpr() instanceof LiteralZero and
+ /* Compliant exception 2: variable's declared type is (u)intptr_t */
+ not (
+ cast.getType() instanceof StdIntIntPtrType and
+ cast.getExpr().getType() instanceof VoidPointerType
+ or
+ cast.getType() instanceof VoidPointerType and
+ cast.getExpr().getType() instanceof StdIntIntPtrType
+ )
+}
+
+from Element elem, string message
+where
+ not isExcluded(elem, Types1Package::convertingAPointerToIntegerOrIntegerToPointerQuery()) and
+ conversionBetweenPointerAndInteger(elem, message)
+select elem, message
diff --git a/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.md b/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.md
new file mode 100644
index 0000000000..f5ddbc1f58
--- /dev/null
+++ b/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.md
@@ -0,0 +1,258 @@
+# MEM30-C: Do not access freed memory
+
+This query implements the CERT-C rule MEM30-C:
+
+> Do not access freed memory
+
+
+## Description
+
+Evaluating a pointer—including dereferencing the pointer, using it as an operand of an arithmetic operation, type casting it, and using it as the right-hand side of an assignment—into memory that has been deallocated by a memory management function is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Pointers to memory that has been deallocated are called *dangling pointers*. Accessing a dangling pointer can result in exploitable [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability).
+
+According to the C Standard, using the value of a pointer that refers to space deallocated by a call to the `free()` or `realloc()` function is undefined behavior. (See [undefined behavior 177](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).)
+
+Reading a pointer to deallocated memory is undefined behavior because the pointer value is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) and might be a [trap representation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-traprepresentation). Fetching a trap representation might perform a hardware trap (but is not required to).
+
+It is at the memory manager's discretion when to reallocate or recycle the freed memory. When memory is freed, all pointers into it become invalid, and its contents might either be returned to the operating system, making the freed space inaccessible, or remain intact and accessible. As a result, the data at the freed location can appear to be valid but change unexpectedly. Consequently, memory must not be written to or read from once it is freed.
+
+## Noncompliant Code Example
+
+This example from Brian Kernighan and Dennis Ritchie \[[Kernighan 1988](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Kernighan88)\] shows both the incorrect and correct techniques for freeing the memory associated with a linked list. In their (intentionally) incorrect example, `p` is freed before `p->next` is executed, so that `p->next` reads memory that has already been freed.
+
+```cpp
+#include
+
+struct node {
+ int value;
+ struct node *next;
+};
+
+void free_list(struct node *head) {
+ for (struct node *p = head; p != NULL; p = p->next) {
+ free(p);
+ }
+}
+```
+
+## Compliant Solution
+
+Kernighan and Ritchie correct this error by storing a reference to `p->next` in `q` before freeing `p`:
+
+```cpp
+#include
+
+struct node {
+ int value;
+ struct node *next;
+};
+
+void free_list(struct node *head) {
+ struct node *q;
+ for (struct node *p = head; p != NULL; p = q) {
+ q = p->next;
+ free(p);
+ }
+}
+```
+
+## Noncompliant Code Example
+
+In this noncompliant code example, `buf` is written to after it has been freed. Write-after-free vulnerabilities can be [exploited](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit) to run arbitrary code with the permissions of the vulnerable process. Typically, allocations and frees are far removed, making it difficult to recognize and diagnose these problems.
+
+```cpp
+#include
+#include
+
+int main(int argc, char *argv[]) {
+ char *return_val = 0;
+ const size_t bufsize = strlen(argv[0]) + 1;
+ char *buf = (char *)malloc(bufsize);
+ if (!buf) {
+ return EXIT_FAILURE;
+ }
+ /* ... */
+ free(buf);
+ /* ... */
+ strcpy(buf, argv[0]);
+ /* ... */
+ return EXIT_SUCCESS;
+}
+```
+
+## Compliant Solution
+
+In this compliant solution, the memory is freed after its final use:
+
+```cpp
+#include
+#include
+
+int main(int argc, char *argv[]) {
+ char *return_val = 0;
+ const size_t bufsize = strlen(argv[0]) + 1;
+ char *buf = (char *)malloc(bufsize);
+ if (!buf) {
+ return EXIT_FAILURE;
+ }
+ /* ... */
+ strcpy(buf, argv[0]);
+ /* ... */
+ free(buf);
+ return EXIT_SUCCESS;
+}
+
+```
+
+## Noncompliant Code Example
+
+In this noncompliant example, `realloc()` may free `c_str1` when it returns a null pointer, resulting in `c_str1` being freed twice. The C Standards Committee's proposed response to [Defect Report \#400](http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm) makes it implementation-defined whether or not the old object is deallocated when `size` is zero and memory for the new object is not allocated. The current implementation of `realloc()` in the GNU C Library and Microsoft Visual Studio's Runtime Library will free `c_str1` and return a null pointer for zero byte allocations. Freeing a pointer twice can result in a potentially exploitable vulnerability commonly referred to as a *double-free vulnerability* \[[Seacord 2013b](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Seacord2013)\].
+
+```cpp
+#include
+
+void f(char *c_str1, size_t size) {
+ char *c_str2 = (char *)realloc(c_str1, size);
+ if (c_str2 == NULL) {
+ free(c_str1);
+ }
+}
+```
+
+## Compliant Solution
+
+This compliant solution does not pass a size argument of zero to the `realloc()` function, eliminating the possibility of `c_str1` being freed twice:
+
+```cpp
+#include
+
+void f(char *c_str1, size_t size) {
+ if (size != 0) {
+ char *c_str2 = (char *)realloc(c_str1, size);
+ if (c_str2 == NULL) {
+ free(c_str1);
+ }
+ }
+ else {
+ free(c_str1);
+ }
+
+}
+```
+If the intent of calling `f()` is to reduce the size of the object, then doing nothing when the size is zero would be unexpected; instead, this compliant solution frees the object.
+
+## Noncompliant Code Example
+
+In this noncompliant example ([CVE-2009-1364](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1364)) from `libwmf` version 0.2.8.4, the return value of `gdRealloc` (a simple wrapper around `realloc()` that reallocates space pointed to by `im->clip->list`) is set to `more`. However, the value of `im->clip->list` is used directly afterwards in the code, and the C Standard specifies that if `realloc()` moves the area pointed to, then the original block is freed. An attacker can then execute arbitrary code by forcing a reallocation (with a sufficient `im->clip->count`) and accessing freed memory \[[xorl 2009](http://xorl.wordpress.com/2009/05/05/cve-2009-1364-libwmf-pointer-use-after-free/)\].
+
+```cpp
+void gdClipSetAdd(gdImagePtr im, gdClipRectanglePtr rect) {
+ gdClipRectanglePtr more;
+ if (im->clip == 0) {
+ /* ... */
+ }
+ if (im->clip->count == im->clip->max) {
+ more = gdRealloc (im->clip->list,(im->clip->max + 8) *
+ sizeof (gdClipRectangle));
+ /*
+ * If the realloc fails, then we have not lost the
+ * im->clip->list value.
+ */
+ if (more == 0) return;
+ im->clip->max += 8;
+ }
+ im->clip->list[im->clip->count] = *rect;
+ im->clip->count++;
+
+}
+```
+
+## Compliant Solution
+
+This compliant solution simply reassigns `im->clip->list` to the value of `more` after the call to `realloc()`:
+
+```cpp
+void gdClipSetAdd(gdImagePtr im, gdClipRectanglePtr rect) {
+ gdClipRectanglePtr more;
+ if (im->clip == 0) {
+ /* ... */
+ }
+ if (im->clip->count == im->clip->max) {
+ more = gdRealloc (im->clip->list,(im->clip->max + 8) *
+ sizeof (gdClipRectangle));
+ if (more == 0) return;
+ im->clip->max += 8;
+ im->clip->list = more;
+ }
+ im->clip->list[im->clip->count] = *rect;
+ im->clip->count++;
+
+}
+```
+
+## Risk Assessment
+
+Reading memory that has already been freed can lead to abnormal program termination and denial-of-service attacks. Writing memory that has already been freed can additionally lead to the execution of arbitrary code with the permissions of the vulnerable process.
+
+Freeing memory multiple times has similar consequences to accessing memory after it is freed. Reading a pointer to deallocated memory is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) because the pointer value is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) and might be a [trap representation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-traprepresentation). When reading from or writing to freed memory does not cause a trap, it may corrupt the underlying data structures that manage the heap in a manner that can be exploited to execute arbitrary code. Alternatively, writing to memory after it has been freed might modify memory that has been reallocated.
+
+Programmers should be wary when freeing memory in a loop or conditional statement; if coded incorrectly, these constructs can lead to double-free vulnerabilities. It is also a common error to misuse the `realloc()` function in a manner that results in double-free vulnerabilities. (See [MEM04-C. Beware of zero-length allocations](https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations).)
+
+